Merge lp:~boiko/telephony-service/voip_support into lp:telephony-service
- voip_support
- Merge into trunk
Status: | Rejected |
---|---|
Rejected by: | Renato Araujo Oliveira Filho |
Proposed branch: | lp:~boiko/telephony-service/voip_support |
Merge into: | lp:telephony-service |
Diff against target: |
1266 lines (+855/-43) 18 files modified
CMakeLists.txt (+4/-2) handler/CMakeLists.txt (+8/-0) handler/TelephonyServiceHandler.client (+9/-0) handler/callagent.cpp (+118/-0) handler/callagent.h (+52/-0) handler/callhandler.cpp (+164/-27) handler/callhandler.h (+8/-1) handler/farstreamchannel.cpp (+330/-0) handler/farstreamchannel.h (+80/-0) handler/handler.cpp (+23/-4) handler/handler.h (+1/-0) handler/main.cpp (+2/-0) libtelephonyservice/accountentry.cpp (+17/-4) libtelephonyservice/callentry.cpp (+15/-3) libtelephonyservice/callentry.h (+1/-1) libtelephonyservice/tonegenerator.cpp (+11/-0) libtelephonyservice/tonegenerator.h (+5/-1) protocols/sip.protocol (+7/-0) |
To merge this branch: | bzr merge lp:~boiko/telephony-service/voip_support |
Related bugs: |
Reviewer | Review Type | Date Requested | Status |
---|---|---|---|
Renato Araujo Oliveira Filho (community) | Disapprove | ||
Review via email: mp+312872@code.launchpad.net |
Commit message
Add VOIP support to telephony-service.
Description of the change
Add VOIP support to telephony-service.
- 967. By Gustavo Pichorim Boiko
-
Remove not needed dependencies
- 968. By Gustavo Pichorim Boiko
-
Fix DTMF, add more gstreamer debugging and add a protocol file for SIP.
- 969. By Gustavo Pichorim Boiko
-
Implement mute for CMs that don't use hardware streaming.
- 970. By Gustavo Pichorim Boiko
-
Play a tone when the call is ringing on the other end.
- 971. By Gustavo Pichorim Boiko
-
Play the correct tone when the call is ringing.
- 972. By Gustavo Pichorim Boiko
-
Play the dialing tone before the ringing one.
Renato Araujo Oliveira Filho (renatofilho) wrote : | # |
Unmerged revisions
- 978. By Gustavo Pichorim Boiko
-
Expose the modem name.
- 977. By Gustavo Pichorim Boiko
-
Select VOIP accounts by default
- 976. By Gustavo Pichorim Boiko
-
When matching SIP contacts, extract the phone number from the SIP URI
- 975. By Gustavo Pichorim Boiko
-
stop the ringing tone when the call ends
- 974. By Gustavo Pichorim Boiko
-
Iterate over the phone accounts only to set the name
- 973. By Gustavo Pichorim Boiko
-
Merge staging
- 972. By Gustavo Pichorim Boiko
-
Play the dialing tone before the ringing one.
- 971. By Gustavo Pichorim Boiko
-
Play the correct tone when the call is ringing.
- 970. By Gustavo Pichorim Boiko
-
Play a tone when the call is ringing on the other end.
- 969. By Gustavo Pichorim Boiko
-
Implement mute for CMs that don't use hardware streaming.
Preview Diff
1 | === modified file 'CMakeLists.txt' |
2 | --- CMakeLists.txt 2016-10-06 19:41:33 +0000 |
3 | +++ CMakeLists.txt 2016-12-17 13:33:18 +0000 |
4 | @@ -35,10 +35,8 @@ |
5 | |
6 | find_package(Qt5Contacts) |
7 | find_package(Qt5DBus) |
8 | -#find_package(Qt5Gui) |
9 | find_package(Qt5Multimedia) |
10 | find_package(Qt5Qml) |
11 | -#find_package(Qt5Quick) |
12 | find_package(Qt5Test) |
13 | find_package(Qt5Feedback) |
14 | find_package(Qt5Network) |
15 | @@ -71,10 +69,14 @@ |
16 | |
17 | find_package(PkgConfig REQUIRED) |
18 | pkg_check_modules(TP_QT5 REQUIRED TelepathyQt5) |
19 | +pkg_check_modules(TP_QT5_FS REQUIRED TelepathyQt5Farstream) |
20 | pkg_check_modules(NOTIFY REQUIRED libnotify) |
21 | pkg_check_modules(MESSAGING_MENU REQUIRED messaging-menu) |
22 | pkg_check_modules(UserMetrics REQUIRED libusermetricsinput-1) |
23 | pkg_check_modules(HISTORY REQUIRED history-service) |
24 | +pkg_check_modules(TPFS REQUIRED telepathy-farstream) |
25 | +pkg_check_modules(GST REQUIRED gstreamer-1.0) |
26 | +pkg_check_modules(FS REQUIRED farstream-0.2) |
27 | |
28 | add_definitions(-DQT_NO_KEYWORDS) |
29 | |
30 | |
31 | === modified file 'handler/CMakeLists.txt' |
32 | --- handler/CMakeLists.txt 2016-07-07 22:16:11 +0000 |
33 | +++ handler/CMakeLists.txt 2016-12-17 13:33:18 +0000 |
34 | @@ -1,7 +1,9 @@ |
35 | |
36 | set(qt_SRCS |
37 | + callagent.cpp |
38 | callhandler.cpp |
39 | chatstartingjob.cpp |
40 | + farstreamchannel.cpp |
41 | handler.cpp |
42 | handlerdbus.cpp |
43 | messagejob.cpp |
44 | @@ -16,6 +18,9 @@ |
45 | |
46 | include_directories( |
47 | ${TP_QT5_INCLUDE_DIRS} |
48 | + ${TPFS_INCLUDE_DIRS} |
49 | + ${FS_INCLUDE_DIRS} |
50 | + ${GST_INCLUDE_DIRS} |
51 | ${CMAKE_SOURCE_DIR}/libtelephonyservice |
52 | ${CMAKE_CURRENT_BINARY_DIR} |
53 | ) |
54 | @@ -25,6 +30,9 @@ |
55 | |
56 | target_link_libraries(telephony-service-handler |
57 | ${TP_QT5_LIBRARIES} |
58 | + ${TP_QT5_FS_LIBRARIES} |
59 | + ${TPFS_LIBRARIES} |
60 | + ${FS_LIBRARIES} |
61 | telephonyservice |
62 | ) |
63 | |
64 | |
65 | === modified file 'handler/TelephonyServiceHandler.client' |
66 | --- handler/TelephonyServiceHandler.client 2016-04-15 22:25:51 +0000 |
67 | +++ handler/TelephonyServiceHandler.client 2016-12-17 13:33:18 +0000 |
68 | @@ -6,3 +6,12 @@ |
69 | |
70 | [org.freedesktop.Telepathy.Client.Handler.HandlerChannelFilter 3] |
71 | org.freedesktop.Telepathy.Channel.ChannelType s=org.freedesktop.Telepathy.Channel.Type.Call1 |
72 | + |
73 | +[org.freedesktop.Telepathy.Client.Handler.HandlerChannelFilter 1] |
74 | +org.freedesktop.Telepathy.Channel.ChannelType s=org.freedesktop.Telepathy.Channel.Type.Call1 |
75 | +org.freedesktop.Telepathy.Channel.Type.Call1.InitialAudio b=true |
76 | + |
77 | +[org.freedesktop.Telepathy.Client.Handler.Capabilities] |
78 | +org.freedesktop.Telepathy.Channel.Type.Call1/audio=true |
79 | +org.freedesktop.Telepathy.Channel.Type.Call1/ice=true |
80 | +org.freedesktop.Telepathy.Channel.Type.Call1/gtalk-p2p=true |
81 | |
82 | === added file 'handler/callagent.cpp' |
83 | --- handler/callagent.cpp 1970-01-01 00:00:00 +0000 |
84 | +++ handler/callagent.cpp 2016-12-17 13:33:18 +0000 |
85 | @@ -0,0 +1,118 @@ |
86 | +/* |
87 | + * Copyright (C) 2014 Canonical, Ltd. |
88 | + * |
89 | + * Authors: |
90 | + * Gustavo Pichorim Boiko <gustavo.boiko@canonical.com> |
91 | + * |
92 | + * This file is part of telephony-service. |
93 | + * |
94 | + * telephony-service is free software; you can redistribute it and/or modify |
95 | + * it under the terms of the GNU General Public License as published by |
96 | + * the Free Software Foundation; version 3. |
97 | + * |
98 | + * telephony-service is distributed in the hope that it will be useful, |
99 | + * but WITHOUT ANY WARRANTY; without even the implied warranty of |
100 | + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
101 | + * GNU General Public License for more details. |
102 | + * |
103 | + * You should have received a copy of the GNU General Public License |
104 | + * along with this program. If not, see <http://www.gnu.org/licenses/>. |
105 | + */ |
106 | + |
107 | +#include "callagent.h" |
108 | +#include <TelepathyQt/CallContent> |
109 | +#include <TelepathyQt/Contact> |
110 | +#include <TelepathyQt/Farstream/Channel> |
111 | +#include <QDebug> |
112 | + |
113 | +CallAgent::CallAgent(const Tp::CallChannelPtr &channel, QObject *parent) : |
114 | + QObject(parent), mChannel(channel), mFarstreamChannel(0) |
115 | +{ |
116 | + connect(mChannel.data(), |
117 | + SIGNAL(invalidated(Tp::DBusProxy*,QString,QString)), |
118 | + SLOT(onCallChannelInvalidated())); |
119 | + connect(mChannel.data(), |
120 | + SIGNAL(callStateChanged(Tp::CallState)), |
121 | + SLOT(onCallStateChanged(Tp::CallState))); |
122 | + connect(mChannel.data(), |
123 | + SIGNAL(contentAdded(Tp::CallContentPtr)), |
124 | + SLOT(onContentAdded(Tp::CallContentPtr))); |
125 | + |
126 | + Q_FOREACH(const Tp::CallContentPtr &content, mChannel->contents()) { |
127 | + onContentAdded(content); |
128 | + } |
129 | + |
130 | + if (!mChannel->handlerStreamingRequired()) { |
131 | + return; |
132 | + } |
133 | + |
134 | + Tp::Farstream::PendingChannel *pendingChannel = Tp::Farstream::createChannel(mChannel); |
135 | + connect(pendingChannel, |
136 | + SIGNAL(finished(Tp::PendingOperation*)), |
137 | + SLOT(onFarstreamChannelCreated(Tp::PendingOperation*))); |
138 | +} |
139 | + |
140 | +CallAgent::~CallAgent() |
141 | +{ |
142 | + if (mFarstreamChannel) { |
143 | + mFarstreamChannel->deleteLater(); |
144 | + } |
145 | +} |
146 | + |
147 | +void CallAgent::setMute(bool mute) |
148 | +{ |
149 | + if (!mFarstreamChannel) { |
150 | + return; |
151 | + } |
152 | + |
153 | + mFarstreamChannel->setMute(mute); |
154 | +} |
155 | + |
156 | +void CallAgent::onCallChannelInvalidated() |
157 | +{ |
158 | + deleteLater(); |
159 | +} |
160 | + |
161 | +void CallAgent::onCallStateChanged(Tp::CallState state) |
162 | +{ |
163 | + if (state == Tp::CallStatePendingInitiator) { |
164 | + mChannel->accept(); |
165 | + } |
166 | +} |
167 | + |
168 | +void CallAgent::onContentAdded(const Tp::CallContentPtr &content) |
169 | +{ |
170 | + if (!mChannel->handlerStreamingRequired()) { |
171 | + return; |
172 | + } |
173 | + |
174 | + qDebug() << "Content Added, name: " << content->name() << " type: " << content->type(); |
175 | + |
176 | + connect(content.data(), |
177 | + SIGNAL(streamAdded(Tp::CallStreamPtr)), |
178 | + SLOT(onStreamAdded(Tp::CallStreamPtr))); |
179 | + |
180 | + Q_FOREACH(const Tp::CallStreamPtr &stream, content->streams()) { |
181 | + onStreamAdded(stream); |
182 | + } |
183 | +} |
184 | + |
185 | +void CallAgent::onStreamAdded(const Tp::CallStreamPtr &stream) |
186 | +{ |
187 | + qDebug() << "Stream present: " << stream->localSendingState(); |
188 | + |
189 | + qDebug() << " members " << stream->remoteMembers().size(); |
190 | + Q_FOREACH(const Tp::ContactPtr contact, stream->remoteMembers()) { |
191 | + qDebug() << " member " << contact->id() << " remoteSendingState=" << stream->remoteSendingState(contact); |
192 | + } |
193 | +} |
194 | + |
195 | +void CallAgent::onFarstreamChannelCreated(Tp::PendingOperation *op) |
196 | +{ |
197 | + Tp::Farstream::PendingChannel *pendingChannel = qobject_cast<Tp::Farstream::PendingChannel*>(op); |
198 | + if (!pendingChannel) { |
199 | + return; |
200 | + } |
201 | + |
202 | + mFarstreamChannel = new FarstreamChannel(pendingChannel->tfChannel(), this); |
203 | +} |
204 | |
205 | === added file 'handler/callagent.h' |
206 | --- handler/callagent.h 1970-01-01 00:00:00 +0000 |
207 | +++ handler/callagent.h 2016-12-17 13:33:18 +0000 |
208 | @@ -0,0 +1,52 @@ |
209 | +/* |
210 | + * Copyright (C) 2014 Canonical, Ltd. |
211 | + * |
212 | + * Authors: |
213 | + * Gustavo Pichorim Boiko <gustavo.boiko@canonical.com> |
214 | + * |
215 | + * This file is part of telephony-service. |
216 | + * |
217 | + * telephony-service is free software; you can redistribute it and/or modify |
218 | + * it under the terms of the GNU General Public License as published by |
219 | + * the Free Software Foundation; version 3. |
220 | + * |
221 | + * telephony-service is distributed in the hope that it will be useful, |
222 | + * but WITHOUT ANY WARRANTY; without even the implied warranty of |
223 | + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
224 | + * GNU General Public License for more details. |
225 | + * |
226 | + * You should have received a copy of the GNU General Public License |
227 | + * along with this program. If not, see <http://www.gnu.org/licenses/>. |
228 | + */ |
229 | + |
230 | +#ifndef CALLAGENT_H |
231 | +#define CALLAGENT_H |
232 | + |
233 | +#include <QObject> |
234 | +#include <TelepathyQt/CallChannel> |
235 | +#include <TelepathyQt/Farstream/Channel> |
236 | +#include "farstreamchannel.h" |
237 | + |
238 | +class CallAgent : public QObject |
239 | +{ |
240 | + Q_OBJECT |
241 | +public: |
242 | + explicit CallAgent(const Tp::CallChannelPtr &channel, QObject *parent = 0); |
243 | + ~CallAgent(); |
244 | + |
245 | + void setMute(bool mute); |
246 | + |
247 | +protected Q_SLOTS: |
248 | + void onCallChannelInvalidated(); |
249 | + void onCallStateChanged(Tp::CallState state); |
250 | + void onContentAdded(const Tp::CallContentPtr &content); |
251 | + void onStreamAdded(const Tp::CallStreamPtr &stream); |
252 | + |
253 | + void onFarstreamChannelCreated(Tp::PendingOperation *op); |
254 | + |
255 | +private: |
256 | + Tp::CallChannelPtr mChannel; |
257 | + FarstreamChannel *mFarstreamChannel; |
258 | +}; |
259 | + |
260 | +#endif // CALLAGENT_H |
261 | |
262 | === modified file 'handler/callhandler.cpp' |
263 | --- handler/callhandler.cpp 2015-07-06 23:32:13 +0000 |
264 | +++ handler/callhandler.cpp 2016-12-17 13:33:18 +0000 |
265 | @@ -20,15 +20,18 @@ |
266 | * along with this program. If not, see <http://www.gnu.org/licenses/>. |
267 | */ |
268 | |
269 | +#include "callagent.h" |
270 | #include "callhandler.h" |
271 | -#include "phoneutils.h" |
272 | #include "telepathyhelper.h" |
273 | #include "accountentry.h" |
274 | #include "tonegenerator.h" |
275 | #include "greetercontacts.h" |
276 | +#include "phoneutils.h" |
277 | #include <TelepathyQt/ContactManager> |
278 | #include <TelepathyQt/PendingContacts> |
279 | #include <TelepathyQt/PendingChannelRequest> |
280 | +#include <TelepathyQt/PendingVariant> |
281 | +#include <memory> |
282 | |
283 | #define TELEPATHY_MUTE_IFACE "org.freedesktop.Telepathy.Call1.Interface.Mute" |
284 | #define DBUS_PROPERTIES_IFACE "org.freedesktop.DBus.Properties" |
285 | @@ -72,8 +75,7 @@ |
286 | bool hasActiveCalls = false; |
287 | |
288 | Q_FOREACH(const Tp::CallChannelPtr channel, mCallChannels) { |
289 | - AccountEntry *accountEntry = TelepathyHelper::instance()->accountForConnection(channel->connection()); |
290 | - bool incoming = channel->initiatorContact() != accountEntry->account()->connection()->selfContact(); |
291 | + bool incoming = isIncoming(channel); |
292 | bool dialing = !incoming && (channel->callState() == Tp::CallStateInitialised); |
293 | bool active = channel->callState() == Tp::CallStateActive; |
294 | |
295 | @@ -94,6 +96,7 @@ |
296 | |
297 | void CallHandler::startCall(const QString &targetId, const QString &accountId) |
298 | { |
299 | + QString finalId = targetId; |
300 | // Request the contact to start audio call |
301 | AccountEntry *accountEntry = TelepathyHelper::instance()->accountForId(accountId); |
302 | if (!accountEntry) { |
303 | @@ -105,7 +108,20 @@ |
304 | return; |
305 | } |
306 | |
307 | - connect(connection->contactManager()->contactsForIdentifiers(QStringList() << targetId), |
308 | + // FIXME: this is a workaround, there might be a better way of handling this. |
309 | + // One idea is to implement the Addressing interface on the SIP connection manager such that |
310 | + // we can request a handle based on the vCard field "tel" |
311 | + if (accountEntry->protocolInfo()->name() == "sip") { |
312 | + // in case this is a SIP call, replace the numbers by a SIP URI |
313 | + QString domain = accountEntry->account()->parameters()["account"].toString(); |
314 | + if (domain.contains("@")) { |
315 | + domain = domain.split("@")[1]; |
316 | + |
317 | + finalId = QString("sip:%1@%2").arg(PhoneUtils::normalizePhoneNumber(targetId)).arg(domain); |
318 | + } |
319 | + } |
320 | + |
321 | + connect(connection->contactManager()->contactsForIdentifiers(QStringList() << finalId), |
322 | SIGNAL(finished(Tp::PendingOperation*)), |
323 | SLOT(onContactsAvailable(Tp::PendingOperation*))); |
324 | } |
325 | @@ -146,9 +162,17 @@ |
326 | return; |
327 | } |
328 | |
329 | - // FIXME: replace by a proper TpQt implementation of mute |
330 | - QDBusInterface muteInterface(channel->busName(), channel->objectPath(), TELEPATHY_MUTE_IFACE); |
331 | - muteInterface.call("RequestMuted", muted); |
332 | + if (channel->handlerStreamingRequired()) { |
333 | + CallAgent *agent = mCallAgents[channel.data()]; |
334 | + if (!agent) { |
335 | + return; |
336 | + } |
337 | + agent->setMute(muted); |
338 | + } else { |
339 | + // FIXME: replace by a proper TpQt implementation of mute |
340 | + QDBusInterface muteInterface(channel->busName(), channel->objectPath(), TELEPATHY_MUTE_IFACE); |
341 | + muteInterface.call("RequestMuted", muted); |
342 | + } |
343 | } |
344 | |
345 | void CallHandler::setActiveAudioOutput(const QString &objectPath, const QString &id) |
346 | @@ -161,26 +185,15 @@ |
347 | |
348 | void CallHandler::sendDTMF(const QString &objectPath, const QString &key) |
349 | { |
350 | - bool ok; |
351 | - Tp::DTMFEvent event = (Tp::DTMFEvent)key.toInt(&ok); |
352 | - if (!ok) { |
353 | - if (!key.compare("*")) { |
354 | - event = Tp::DTMFEventAsterisk; |
355 | - } else if (!key.compare("#")) { |
356 | - event = Tp::DTMFEventHash; |
357 | - } else { |
358 | - qWarning() << "Tone not recognized. DTMF failed"; |
359 | - return; |
360 | - } |
361 | - } |
362 | /* |
363 | - * play locally (via tone generator) only if we are on a call, or if this is |
364 | + * play locally (via tone generator) only if we are on a call, or if this is |
365 | * dialpad sounds |
366 | */ |
367 | - if (GreeterContacts::instance()->dialpadSoundsEnabled() && |
368 | + int event = toDTMFEvent(key); |
369 | + if (GreeterContacts::instance()->dialpadSoundsEnabled() && |
370 | !GreeterContacts::instance()->silentMode() && objectPath.isEmpty() |
371 | || !objectPath.isEmpty()) { |
372 | - ToneGenerator::instance()->playDTMFTone((uint)event); |
373 | + ToneGenerator::instance()->playDTMFTone(event); |
374 | } |
375 | |
376 | Tp::CallChannelPtr channel = callFromObjectPath(objectPath); |
377 | @@ -190,14 +203,15 @@ |
378 | |
379 | // save the dtmfString to send to clients that request it |
380 | QString dtmfString = channel->property("dtmfString").toString(); |
381 | + QString pendingDTMF = channel->property("pendingDTMF").toString(); |
382 | + pendingDTMF += key; |
383 | dtmfString += key; |
384 | channel->setProperty("dtmfString", dtmfString); |
385 | + channel->setProperty("pendingDTMF", pendingDTMF); |
386 | |
387 | - Q_FOREACH(const Tp::CallContentPtr &content, channel->contents()) { |
388 | - if (content->supportsDTMF()) { |
389 | - /* send DTMF to network (via telepathy and oFono) */ |
390 | - content->startDTMFTone(event); |
391 | - } |
392 | + // if there is only one pending DTMF event, start playing it |
393 | + if (pendingDTMF.length() == 1) { |
394 | + playNextDTMFTone(channel); |
395 | } |
396 | |
397 | Q_EMIT callPropertiesChanged(channel->objectPath(), getCallProperties(channel->objectPath())); |
398 | @@ -273,6 +287,8 @@ |
399 | |
400 | if (channel->callState() == Tp::CallStateActive) { |
401 | channel->setProperty("activeTimestamp", QDateTime::currentDateTimeUtc()); |
402 | + } else if (channel->callState() == Tp::CallStatePendingInitiator) { |
403 | + channel->accept(); |
404 | } |
405 | |
406 | connect(channel.data(), |
407 | @@ -282,6 +298,10 @@ |
408 | SIGNAL(callStateChanged(Tp::CallState)), |
409 | SLOT(onCallStateChanged(Tp::CallState))); |
410 | |
411 | + // FIXME: save this to a list |
412 | + CallAgent *agent = new CallAgent(channel, this); |
413 | + mCallAgents[channel.data()] = agent; |
414 | + |
415 | mCallChannels.append(channel); |
416 | Q_EMIT callPropertiesChanged(channel->objectPath(), getCallProperties(channel->objectPath())); |
417 | } |
418 | @@ -331,6 +351,10 @@ |
419 | } |
420 | |
421 | mCallChannels.removeAll(channel); |
422 | + if (mCallAgents.contains(channel.data())) { |
423 | + CallAgent *agent = mCallAgents.take(channel.data()); |
424 | + agent->deleteLater(); |
425 | + } |
426 | |
427 | if (mCallChannels.isEmpty() && !mHangupRequested) { |
428 | ToneGenerator::instance()->playCallEndedTone(); |
429 | @@ -346,7 +370,22 @@ |
430 | } |
431 | |
432 | switch (state) { |
433 | + case Tp::CallStatePendingInitiator: |
434 | + case Tp::CallStateInitialising: |
435 | + if (!isIncoming(channel) && channel->handlerStreamingRequired()) { |
436 | + ToneGenerator::instance()->playDialingTone(); |
437 | + } |
438 | + break; |
439 | + case Tp::CallStateInitialised: |
440 | + if (!isIncoming(channel) && channel->handlerStreamingRequired()) { |
441 | + ToneGenerator::instance()->stopTone(); |
442 | + ToneGenerator::instance()->playRingingTone(); |
443 | + } |
444 | + break; |
445 | case Tp::CallStateActive: |
446 | + if (channel->handlerStreamingRequired()) { |
447 | + ToneGenerator::instance()->stopTone(); |
448 | + } |
449 | channel->setProperty("activeTimestamp", QDateTime::currentDateTimeUtc()); |
450 | Q_EMIT callPropertiesChanged(channel->objectPath(), getCallProperties(channel->objectPath())); |
451 | break; |
452 | @@ -387,3 +426,101 @@ |
453 | |
454 | return channel; |
455 | } |
456 | + |
457 | +void CallHandler::playNextDTMFTone(Tp::CallChannelPtr channel) |
458 | +{ |
459 | + // the channel might have been closed already |
460 | + if (!channel) { |
461 | + return; |
462 | + } |
463 | + |
464 | + QString pendingDTMF = channel->property("pendingDTMF").toString(); |
465 | + QString key = ""; |
466 | + if (!pendingDTMF.isEmpty()) { |
467 | + key = pendingDTMF[0]; |
468 | + } |
469 | + |
470 | + int event = toDTMFEvent(key); |
471 | + |
472 | + Q_FOREACH(const Tp::CallContentPtr &content, channel->contents()) { |
473 | + if (content->supportsDTMF()) { |
474 | + |
475 | + /* stop any previous DTMF tone before sending the new one*/ |
476 | + connect(content->stopDTMFTone(), &Tp::PendingOperation::finished, [=](Tp::PendingOperation *op){ |
477 | + // in case stopDTMFTone, it might mean the service automatically stops the tone, |
478 | + // so try playing the next one |
479 | + if (op->isError()) { |
480 | + /* send DTMF to network (via telepathy) */ |
481 | + if (event >= 0) { |
482 | + content->startDTMFTone((Tp::DTMFEvent)event); |
483 | + } |
484 | + triggerNextDTMFTone(channel); |
485 | + return; |
486 | + } |
487 | + |
488 | + Tp::Client::CallContentInterfaceDTMFInterface *dtmfInterface = content->interface<Tp::Client::CallContentInterfaceDTMFInterface>(); |
489 | + Tp::PendingVariant *pv = dtmfInterface->requestPropertyCurrentlySendingTones(); |
490 | + connect(pv, &Tp::PendingOperation::finished, [=](){ |
491 | + bool sendingTones = pv->result().toBool(); |
492 | + // if we already stopped sending tones, we can send the next one |
493 | + if (!sendingTones) { |
494 | + /* send DTMF to network (via telepathy) */ |
495 | + if (event >= 0) { |
496 | + content->startDTMFTone((Tp::DTMFEvent)event); |
497 | + } |
498 | + triggerNextDTMFTone(channel); |
499 | + return; |
500 | + } |
501 | + |
502 | + // in case the previous tone is not finished, we need to wait for it |
503 | + auto conn = std::make_shared<QMetaObject::Connection>(); |
504 | + *conn = connect(dtmfInterface, &Tp::Client::CallContentInterfaceDTMFInterface::StoppedTones, [=](){ |
505 | + QObject::disconnect(*conn); |
506 | + |
507 | + /* send DTMF to network (via telepathy) */ |
508 | + if (event >= 0) { |
509 | + content->startDTMFTone((Tp::DTMFEvent)event); |
510 | + } |
511 | + triggerNextDTMFTone(channel); |
512 | + }); |
513 | + }); |
514 | + |
515 | + }); |
516 | + } |
517 | + } |
518 | +} |
519 | + |
520 | +void CallHandler::triggerNextDTMFTone(Tp::CallChannelPtr channel) |
521 | +{ |
522 | + QTimer::singleShot(250, [=](){ |
523 | + QString pendingDTMF = channel->property("pendingDTMF").toString(); |
524 | + if (pendingDTMF.isEmpty()) { |
525 | + return; |
526 | + } |
527 | + pendingDTMF.remove(0, 1); |
528 | + channel->setProperty("pendingDTMF", pendingDTMF); |
529 | + playNextDTMFTone(channel); |
530 | + }); |
531 | +} |
532 | + |
533 | +int CallHandler::toDTMFEvent(const QString &key) |
534 | +{ |
535 | + bool ok; |
536 | + int ev = key.toInt(&ok); |
537 | + if (!ok) { |
538 | + if (key == "*") { |
539 | + ev = Tp::DTMFEventAsterisk; |
540 | + } else if (key == "#") { |
541 | + ev = Tp::DTMFEventHash; |
542 | + } else { |
543 | + ev = -1; |
544 | + } |
545 | + } |
546 | + return ev; |
547 | +} |
548 | + |
549 | +bool CallHandler::isIncoming(const Tp::CallChannelPtr &channel) const |
550 | +{ |
551 | + AccountEntry *accountEntry = TelepathyHelper::instance()->accountForConnection(channel->connection()); |
552 | + return channel->initiatorContact() != accountEntry->account()->connection()->selfContact(); |
553 | +} |
554 | |
555 | === modified file 'handler/callhandler.h' |
556 | --- handler/callhandler.h 2015-03-04 18:02:13 +0000 |
557 | +++ handler/callhandler.h 2016-12-17 13:33:18 +0000 |
558 | @@ -1,5 +1,5 @@ |
559 | /* |
560 | - * Copyright (C) 2012-2013 Canonical, Ltd. |
561 | + * Copyright (C) 2012-2014 Canonical, Ltd. |
562 | * |
563 | * Authors: |
564 | * Gustavo Pichorim Boiko <gustavo.boiko@canonical.com> |
565 | @@ -28,6 +28,7 @@ |
566 | #include <TelepathyQt/CallChannel> |
567 | |
568 | class TelepathyHelper; |
569 | +class CallAgent; |
570 | |
571 | class CallHandler : public QObject |
572 | { |
573 | @@ -61,6 +62,11 @@ |
574 | Tp::CallChannelPtr existingCall(const QString &targetId); |
575 | Tp::CallChannelPtr callFromObjectPath(const QString &objectPath); |
576 | |
577 | + void playNextDTMFTone(Tp::CallChannelPtr channel); |
578 | + void triggerNextDTMFTone(Tp::CallChannelPtr channel); |
579 | + static int toDTMFEvent(const QString &key); |
580 | + bool isIncoming(const Tp::CallChannelPtr &channel) const; |
581 | + |
582 | protected Q_SLOTS: |
583 | void onContactsAvailable(Tp::PendingOperation *op); |
584 | void onCallHangupFinished(Tp::PendingOperation *op); |
585 | @@ -72,6 +78,7 @@ |
586 | |
587 | QMap<QString, Tp::ContactPtr> mContacts; |
588 | QList<Tp::CallChannelPtr> mCallChannels; |
589 | + QMap<Tp::CallChannel*,CallAgent*> mCallAgents; |
590 | QMap<Tp::PendingOperation*,Tp::CallChannelPtr> mClosingChannels; |
591 | bool mHangupRequested; |
592 | }; |
593 | |
594 | === added file 'handler/farstreamchannel.cpp' |
595 | --- handler/farstreamchannel.cpp 1970-01-01 00:00:00 +0000 |
596 | +++ handler/farstreamchannel.cpp 2016-12-17 13:33:18 +0000 |
597 | @@ -0,0 +1,330 @@ |
598 | +/* |
599 | + * Copyright (C) 2014 Canonical, Ltd. |
600 | + * |
601 | + * Authors: |
602 | + * Gustavo Pichorim Boiko <gustavo.boiko@canonical.com> |
603 | + * |
604 | + * This file is part of telephony-service. |
605 | + * |
606 | + * telephony-service is free software; you can redistribute it and/or modify |
607 | + * it under the terms of the GNU General Public License as published by |
608 | + * the Free Software Foundation; version 3. |
609 | + * |
610 | + * telephony-service is distributed in the hope that it will be useful, |
611 | + * but WITHOUT ANY WARRANTY; without even the implied warranty of |
612 | + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
613 | + * GNU General Public License for more details. |
614 | + * |
615 | + * You should have received a copy of the GNU General Public License |
616 | + * along with this program. If not, see <http://www.gnu.org/licenses/>. |
617 | + */ |
618 | + |
619 | +#include "farstreamchannel.h" |
620 | +#include <farstream/fs-utils.h> |
621 | +#include <QDebug> |
622 | + |
623 | +FarstreamChannel::FarstreamChannel(TfChannel *channel, QObject *parent) : |
624 | + mChannel(channel), QObject(parent), mPipeline(0), mBus(0), mBusSource(0), |
625 | + mConferenceAddedSignal(0), mConferenceRemovedSignal(0), mContentAddedSignal(0), |
626 | + mContentRemovedSignal(0), mAudioInput(0), mAudioOutput(0) |
627 | +{ |
628 | + qDebug() << __PRETTY_FUNCTION__; |
629 | + initialize(); |
630 | +} |
631 | + |
632 | +FarstreamChannel::~FarstreamChannel() |
633 | +{ |
634 | + // stop audio input and output |
635 | + if (mAudioInput) { |
636 | + setState(mAudioInput, GST_STATE_NULL); |
637 | + gst_object_unref(mAudioInput); |
638 | + } |
639 | + |
640 | + if (mAudioOutput) { |
641 | + setState(mAudioOutput, GST_STATE_NULL); |
642 | + gst_object_unref(mAudioOutput); |
643 | + } |
644 | + |
645 | + // now clear the notifiers |
646 | + Q_FOREACH(FsElementAddedNotifier *notifier, mNotifiers) { |
647 | + fs_element_added_notifier_remove(notifier, GST_BIN(mPipeline)); |
648 | + g_object_unref(notifier); |
649 | + } |
650 | + mNotifiers.clear(); |
651 | + |
652 | + // clear the bus stuff |
653 | + if (mBusSource) { |
654 | + g_source_remove(mBusSource); |
655 | + } |
656 | + |
657 | + if (mBus) { |
658 | + gst_object_unref(mBus); |
659 | + } |
660 | + |
661 | + // and finally the pipeline |
662 | + if (mPipeline) { |
663 | + setState(mPipeline, GST_STATE_NULL); |
664 | + gst_object_unref(mPipeline); |
665 | + } |
666 | +} |
667 | + |
668 | +void FarstreamChannel::setMute(bool mute) |
669 | +{ |
670 | + GstElement *input_volume = gst_bin_get_by_name(GST_BIN(mPipeline), "input_volume"); |
671 | + g_object_set(input_volume, "mute", mute, NULL); |
672 | + g_object_unref(input_volume); |
673 | +} |
674 | + |
675 | +void FarstreamChannel::initialize() |
676 | +{ |
677 | + qDebug() << __PRETTY_FUNCTION__; |
678 | + // connect all the signals |
679 | + mConferenceAddedSignal = g_signal_connect(mChannel, "fs-conference-added", |
680 | + G_CALLBACK(&FarstreamChannel::onConferenceAdded), |
681 | + this); |
682 | + mConferenceRemovedSignal = g_signal_connect(mChannel, "fs-conference-removed", |
683 | + G_CALLBACK(&FarstreamChannel::onConferenceRemoved), |
684 | + this); |
685 | + mContentAddedSignal = g_signal_connect(mChannel, "content-added", |
686 | + G_CALLBACK(&FarstreamChannel::onContentAdded), |
687 | + this); |
688 | + mContentRemovedSignal = g_signal_connect(mChannel, "content-removed", |
689 | + G_CALLBACK(&FarstreamChannel::onContentRemoved), |
690 | + this); |
691 | + |
692 | + // and initialize the gstreamer pipeline |
693 | + mPipeline = gst_pipeline_new(NULL); |
694 | + if (!mPipeline) { |
695 | + qCritical() << "Failed to create GStreamer pipeline."; |
696 | + return; |
697 | + } |
698 | + |
699 | + mBus = gst_pipeline_get_bus(GST_PIPELINE(mPipeline)); |
700 | + if (!mBus) { |
701 | + qCritical() << "Failed to get GStreamer pipeline bus."; |
702 | + return; |
703 | + } |
704 | + |
705 | + mBusSource = gst_bus_add_watch(mBus, (GstBusFunc) &FarstreamChannel::onBusWatch, this); |
706 | + |
707 | + if (!setState(mPipeline, GST_STATE_PLAYING)) { |
708 | + return; |
709 | + } |
710 | +} |
711 | + |
712 | +GstElement *FarstreamChannel::initializeAudioSource(TfContent *content) |
713 | +{ |
714 | + qDebug() << __PRETTY_FUNCTION__; |
715 | + GstElement *element = gst_parse_bin_from_description ("pulsesrc ! audio/x-raw, rate=8000 ! queue" |
716 | + " ! audioconvert ! audioresample" |
717 | + " ! volume name=input_volume ! audioconvert ", |
718 | + TRUE, NULL); |
719 | + gint input_volume = 0; |
720 | + g_object_get (content, "requested-input-volume", &input_volume, NULL); |
721 | + |
722 | + if (input_volume >= 0) { |
723 | + GstElement *volume = gst_bin_get_by_name (GST_BIN (element), "input_volume"); |
724 | + g_object_set (volume, "volume", (double)input_volume / 255.0, NULL); |
725 | + gst_object_unref (volume); |
726 | + } |
727 | + |
728 | + // FIXME: we probably need to handle input volume request changes |
729 | + mAudioOutput = element; |
730 | + return element; |
731 | +} |
732 | + |
733 | +bool FarstreamChannel::addToPipeline(GstElement *element) |
734 | +{ |
735 | + qDebug() << __PRETTY_FUNCTION__ << GST_ELEMENT_NAME(element); |
736 | + if (!mPipeline) { |
737 | + qWarning() << "No gstreamer pipeline found."; |
738 | + return false; |
739 | + } |
740 | + |
741 | + if (!gst_bin_add(GST_BIN(mPipeline), element)) { |
742 | + qCritical() << "Failed to add bin" << GST_ELEMENT_NAME(element) << "to pipeline."; |
743 | + return false; |
744 | + } |
745 | + qDebug() << "Succeeded adding to pipeline!"; |
746 | + return true; |
747 | +} |
748 | + |
749 | +void FarstreamChannel::removeFromPipeline(GstElement *element) |
750 | +{ |
751 | + qDebug() << __PRETTY_FUNCTION__ << GST_ELEMENT_NAME(element); |
752 | + gst_element_set_locked_state(element, TRUE); |
753 | + setState(element, GST_STATE_NULL); |
754 | + gst_bin_remove (GST_BIN (mPipeline), element); |
755 | +} |
756 | + |
757 | +bool FarstreamChannel::setState(GstElement *element, GstState state) |
758 | +{ |
759 | + qDebug() << __PRETTY_FUNCTION__ << GST_ELEMENT_NAME(element) << gst_element_state_get_name(state); |
760 | + GstStateChangeReturn result = gst_element_set_state(element, state); |
761 | + if (result == GST_STATE_CHANGE_FAILURE) { |
762 | + qCritical() << "Failed to set GStreamer element" << GST_ELEMENT_NAME(element) << "state to" << gst_element_state_get_name(state); |
763 | + return false; |
764 | + } |
765 | + qDebug() << "Succeeded playing!"; |
766 | + return true; |
767 | +} |
768 | + |
769 | +gboolean FarstreamChannel::onBusWatch(GstBus *bus, GstMessage *message, FarstreamChannel *self) |
770 | +{ |
771 | + Q_UNUSED(bus) |
772 | + if (!self->mChannel) { |
773 | + return TRUE; |
774 | + } |
775 | + |
776 | + // FIXME: maybe we need to do some error handling here? |
777 | + tf_channel_bus_message(self->mChannel, message); |
778 | + return TRUE; |
779 | +} |
780 | + |
781 | +void FarstreamChannel::onConferenceAdded(TfChannel *channel, FsConference *conference, FarstreamChannel *self) |
782 | +{ |
783 | + qDebug() << __PRETTY_FUNCTION__; |
784 | + Q_UNUSED(channel) |
785 | + |
786 | + /* Add notifier to set the various element properties as needed */ |
787 | + GKeyFile *keyfile = fs_utils_get_default_element_properties (GST_ELEMENT(conference)); |
788 | + if (keyfile != NULL) { |
789 | + qDebug() << "Loaded default properties for" << GST_ELEMENT_NAME(conference); |
790 | + FsElementAddedNotifier *notifier = fs_element_added_notifier_new(); |
791 | + fs_element_added_notifier_set_properties_from_keyfile(notifier, keyfile); |
792 | + fs_element_added_notifier_add(notifier, GST_BIN(self->mPipeline)); |
793 | + |
794 | + // FIXME: right now we are leaking the notifiers, check when to remove them |
795 | + self->mNotifiers.append(notifier); |
796 | + } |
797 | + |
798 | + if (!self->addToPipeline(GST_ELEMENT(conference))) { |
799 | + return; |
800 | + } |
801 | + |
802 | + self->setState(GST_ELEMENT(conference), GST_STATE_PLAYING); |
803 | +} |
804 | + |
805 | +void FarstreamChannel::onConferenceRemoved(TfChannel *channel, FsConference *conference, FarstreamChannel *self) |
806 | +{ |
807 | + qDebug() << __PRETTY_FUNCTION__; |
808 | + Q_UNUSED(channel); |
809 | + |
810 | + // just remove the conference from the pipeline |
811 | + self->removeFromPipeline(GST_ELEMENT(conference)); |
812 | +} |
813 | + |
814 | +void FarstreamChannel::onContentAdded(TfChannel *channel, TfContent *content, FarstreamChannel *self) |
815 | +{ |
816 | + qDebug() << __PRETTY_FUNCTION__; |
817 | + Q_UNUSED(channel) |
818 | + |
819 | + g_signal_connect(content, "src-pad-added", |
820 | + G_CALLBACK(&FarstreamChannel::onSrcPadAdded), self); |
821 | + g_signal_connect(content, "start-sending", |
822 | + G_CALLBACK(&FarstreamChannel::onStartSending), self); |
823 | + g_signal_connect(content, "stop-sending", |
824 | + G_CALLBACK(&FarstreamChannel::onStopSending), self); |
825 | +} |
826 | + |
827 | +void FarstreamChannel::onContentRemoved(TfChannel *channel, TfContent *content, FarstreamChannel *self) |
828 | +{ |
829 | + qDebug() << __PRETTY_FUNCTION__; |
830 | + // FIXME: implement |
831 | +} |
832 | + |
833 | +bool FarstreamChannel::onStartSending(TfContent *content, FarstreamChannel *self) |
834 | +{ |
835 | + qDebug() << __PRETTY_FUNCTION__; |
836 | + GstPad *sinkPad; |
837 | + FsMediaType mediaType; |
838 | + GstElement *element; |
839 | + |
840 | + g_object_get (content, "sink-pad", &sinkPad, "media-type", &mediaType, NULL); |
841 | + |
842 | + switch (mediaType) { |
843 | + case FS_MEDIA_TYPE_AUDIO: |
844 | + element = self->initializeAudioSource(content); |
845 | + break; |
846 | + // FIXME: add video support |
847 | + default: |
848 | + qWarning() << "Unsupported media type:" << mediaType; |
849 | + g_object_unref(sinkPad); |
850 | + return false; |
851 | + } |
852 | + |
853 | + if (!self->addToPipeline(element)) { |
854 | + g_object_unref(sinkPad); |
855 | + return false; |
856 | + } |
857 | + |
858 | + GstPad *sourcePad = gst_element_get_static_pad (element, "src"); |
859 | + if (GST_PAD_LINK_FAILED (gst_pad_link (sourcePad, sinkPad))) { |
860 | + qCritical() << "Failed to link source pad to content's sink pad"; |
861 | + g_object_unref(sinkPad); |
862 | + g_object_unref(sourcePad); |
863 | + return false; |
864 | + } |
865 | + |
866 | + self->setState(element, GST_STATE_PLAYING); |
867 | + self->mAudioInput = element; |
868 | + |
869 | + g_object_unref(sinkPad); |
870 | + g_object_unref(sourcePad); |
871 | + |
872 | + qDebug() << "BLABLA generating dot file"; |
873 | + GST_DEBUG_BIN_TO_DOT_FILE (GST_BIN (self->mPipeline), |
874 | + GST_DEBUG_GRAPH_SHOW_ALL, "telephony-service-gst"); |
875 | + qDebug() << "BLABLA done!"; |
876 | + return true; |
877 | +} |
878 | + |
879 | +void FarstreamChannel::onStopSending(TfContent *content, FarstreamChannel *self) |
880 | +{ |
881 | + qDebug() << __PRETTY_FUNCTION__; |
882 | + // FIXME: implement |
883 | +} |
884 | + |
885 | +void FarstreamChannel::onSrcPadAdded(TfContent *content, uint handle, FsStream *stream, GstPad *pad, FsCodec *codec, FarstreamChannel *self) |
886 | +{ |
887 | + qDebug() << __PRETTY_FUNCTION__; |
888 | + gchar *codecString = fs_codec_to_string (codec); |
889 | + qDebug() << __PRETTY_FUNCTION__ << "Codec:" << codecString; |
890 | + |
891 | + FsMediaType mediaType; |
892 | + GstElement *element; |
893 | + |
894 | + g_object_get (content, "media-type", &mediaType, NULL); |
895 | + |
896 | + switch (mediaType) { |
897 | + case FS_MEDIA_TYPE_AUDIO: { |
898 | + QString outputVolume = QString("output_volume%1").arg(codecString); |
899 | + QString description = QString("audioconvert ! audioresample " |
900 | + "! volume name=\"%1\" " |
901 | + "! audioconvert ! autoaudiosink").arg(outputVolume); |
902 | + element = gst_parse_bin_from_description (description.toUtf8().data(), TRUE, NULL); |
903 | + GstElement *volume = gst_bin_get_by_name (GST_BIN (element), outputVolume.toUtf8().data()); |
904 | + |
905 | + // FIXME: we need to handle volume request changes in the volume element |
906 | + gst_object_unref (volume); |
907 | + break; |
908 | + } |
909 | + // FIXME: handle video |
910 | + default: |
911 | + qWarning() << "Unsupported media type:" << mediaType; |
912 | + return; |
913 | + } |
914 | + |
915 | + if (!self->addToPipeline(element)) { |
916 | + return; |
917 | + } |
918 | + |
919 | + GstPad *sinkPad = gst_element_get_static_pad (element, "sink"); |
920 | + if (GST_PAD_LINK_FAILED (gst_pad_link (pad, sinkPad))) { |
921 | + qCritical() << "Failed to link content's source pad to local sink pad"; |
922 | + } |
923 | + |
924 | + self->setState(element, GST_STATE_PLAYING); |
925 | + |
926 | + g_object_unref (sinkPad); |
927 | +} |
928 | |
929 | === added file 'handler/farstreamchannel.h' |
930 | --- handler/farstreamchannel.h 1970-01-01 00:00:00 +0000 |
931 | +++ handler/farstreamchannel.h 2016-12-17 13:33:18 +0000 |
932 | @@ -0,0 +1,80 @@ |
933 | +/* |
934 | + * Copyright (C) 2014 Canonical, Ltd. |
935 | + * |
936 | + * Authors: |
937 | + * Gustavo Pichorim Boiko <gustavo.boiko@canonical.com> |
938 | + * |
939 | + * This file is part of telephony-service. |
940 | + * |
941 | + * telephony-service is free software; you can redistribute it and/or modify |
942 | + * it under the terms of the GNU General Public License as published by |
943 | + * the Free Software Foundation; version 3. |
944 | + * |
945 | + * telephony-service is distributed in the hope that it will be useful, |
946 | + * but WITHOUT ANY WARRANTY; without even the implied warranty of |
947 | + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
948 | + * GNU General Public License for more details. |
949 | + * |
950 | + * You should have received a copy of the GNU General Public License |
951 | + * along with this program. If not, see <http://www.gnu.org/licenses/>. |
952 | + */ |
953 | + |
954 | +#ifndef FARSTREAMCHANNEL_H |
955 | +#define FARSTREAMCHANNEL_H |
956 | + |
957 | +#include <QObject> |
958 | +#include <QList> |
959 | +#include <telepathy-farstream/telepathy-farstream.h> |
960 | +#include <farstream/fs-conference.h> |
961 | +#include <farstream/fs-stream.h> |
962 | +#include <farstream/fs-element-added-notifier.h> |
963 | + |
964 | +class FarstreamChannel : public QObject |
965 | +{ |
966 | + Q_OBJECT |
967 | +public: |
968 | + explicit FarstreamChannel(TfChannel *channel, QObject *parent = 0); |
969 | + ~FarstreamChannel(); |
970 | + |
971 | + void setMute(bool mute); |
972 | + |
973 | +protected: |
974 | + void initialize(); |
975 | + GstElement *initializeAudioSource(TfContent *content); |
976 | + |
977 | + // gstreamer helpers |
978 | + bool addToPipeline(GstElement *element); |
979 | + void removeFromPipeline(GstElement *bin); |
980 | + bool setState(GstElement *element, GstState state); |
981 | + |
982 | + // glib signal handlers |
983 | + static gboolean onBusWatch(GstBus *bus, GstMessage *message, FarstreamChannel *self); |
984 | + static void onConferenceAdded(TfChannel *channel, FsConference *conference, FarstreamChannel *self); |
985 | + static void onConferenceRemoved(TfChannel *channel, FsConference *conference, FarstreamChannel *self); |
986 | + static void onContentAdded(TfChannel *channel, TfContent * content, FarstreamChannel *self); |
987 | + static void onContentRemoved(TfChannel *channel, TfContent * content, FarstreamChannel *self); |
988 | + static bool onStartSending(TfContent *content, FarstreamChannel *self); |
989 | + static void onStopSending(TfContent *content, FarstreamChannel *self); |
990 | + static void onSrcPadAdded(TfContent *content, uint handle, FsStream *stream, GstPad *pad, FsCodec *codec, FarstreamChannel *self); |
991 | + |
992 | +private: |
993 | + TfChannel *mChannel; |
994 | + |
995 | + // gstreamer stuff |
996 | + GstElement *mPipeline; |
997 | + GstBus *mBus; |
998 | + uint mBusSource; |
999 | + GstElement *mAudioInput; |
1000 | + GstElement *mAudioOutput; |
1001 | + |
1002 | + // signal IDs |
1003 | + gulong mConferenceAddedSignal; |
1004 | + gulong mConferenceRemovedSignal; |
1005 | + gulong mContentAddedSignal; |
1006 | + gulong mContentRemovedSignal; |
1007 | + |
1008 | + // farstream stuff |
1009 | + QList<FsElementAddedNotifier*> mNotifiers; |
1010 | +}; |
1011 | + |
1012 | +#endif // FARSTREAMCHANNEL_H |
1013 | |
1014 | === modified file 'handler/handler.cpp' |
1015 | --- handler/handler.cpp 2016-11-23 19:28:18 +0000 |
1016 | +++ handler/handler.cpp 2016-12-17 13:33:18 +0000 |
1017 | @@ -32,7 +32,7 @@ |
1018 | #include <TelepathyQt/PendingReady> |
1019 | |
1020 | Handler::Handler(QObject *parent) |
1021 | - : QObject(parent), Tp::AbstractClientHandler(channelFilters()) |
1022 | + : QObject(parent), Tp::AbstractClientHandler(channelFilters(), capabilities()) |
1023 | { |
1024 | } |
1025 | |
1026 | @@ -102,9 +102,22 @@ |
1027 | specList << Tp::ChannelClassSpec::textChatroom(); |
1028 | specList << Tp::ChannelClassSpec::unnamedTextChat(); |
1029 | |
1030 | + QVariantMap props; |
1031 | + props[TP_QT_IFACE_CHANNEL_TYPE_CALL + ".InitialAudio"] = true; |
1032 | + specList << Tp::ChannelClassSpec::audioCall(props); |
1033 | + |
1034 | return specList; |
1035 | } |
1036 | |
1037 | +Tp::AbstractClientHandler::Capabilities Handler::capabilities() |
1038 | +{ |
1039 | + QStringList caps; |
1040 | + caps << TP_QT_IFACE_CHANNEL_TYPE_CALL + "/shm" |
1041 | + << TP_QT_IFACE_CHANNEL_TYPE_CALL + "/ice" |
1042 | + << TP_QT_IFACE_CHANNEL_TYPE_CALL + "/gtalk-p2p"; |
1043 | + return Tp::AbstractClientHandler::Capabilities(caps); |
1044 | +} |
1045 | + |
1046 | void Handler::onTextChannelReady(Tp::PendingOperation *op) |
1047 | { |
1048 | Tp::PendingReady *pr = qobject_cast<Tp::PendingReady*>(op); |
1049 | @@ -133,6 +146,7 @@ |
1050 | |
1051 | void Handler::onCallChannelReady(Tp::PendingOperation *op) |
1052 | { |
1053 | + qDebug() << "BLABLA" << __PRETTY_FUNCTION__; |
1054 | Tp::PendingReady *pr = qobject_cast<Tp::PendingReady*>(op); |
1055 | |
1056 | if (!pr) { |
1057 | @@ -155,11 +169,16 @@ |
1058 | // if the call is neither Accepted nor Active, it means it got dispatched directly to the handler without passing |
1059 | // through any approver. For phone calls, this would mean calls getting auto-accepted which is not desirable |
1060 | // so we return an error here |
1061 | - bool incoming = false; |
1062 | + bool incoming = !callChannel->isRequested(); |
1063 | + qDebug() << "BLABLA Is requested:" << !incoming; |
1064 | AccountEntry *accountEntry = TelepathyHelper::instance()->accountForConnection(callChannel->connection()); |
1065 | - if (accountEntry) { |
1066 | - incoming = callChannel->initiatorContact() != accountEntry->account()->connection()->selfContact(); |
1067 | + qDebug() << "BLABLA accountEntry:" << accountEntry; |
1068 | + if (accountEntry && |
1069 | + !callChannel->initiatorContact().isNull() && |
1070 | + callChannel->initiatorContact() != accountEntry->account()->connection()->selfContact()) { |
1071 | + incoming = true; |
1072 | } |
1073 | + qDebug() << "BLABLA incoming:" << incoming; |
1074 | if (incoming && callChannel->callState() != Tp::CallStateAccepted && callChannel->callState() != Tp::CallStateActive) { |
1075 | qWarning() << "Available channel was not approved by telephony-service-approver, ignoring it."; |
1076 | if (context) { |
1077 | |
1078 | === modified file 'handler/handler.h' |
1079 | --- handler/handler.h 2015-07-01 22:04:30 +0000 |
1080 | +++ handler/handler.h 2016-12-17 13:33:18 +0000 |
1081 | @@ -45,6 +45,7 @@ |
1082 | const QDateTime &userActionTime, |
1083 | const Tp::AbstractClientHandler::HandlerInfo &handlerInfo); |
1084 | Tp::ChannelClassSpecList channelFilters(); |
1085 | + Tp::AbstractClientHandler::Capabilities capabilities(); |
1086 | |
1087 | Q_SIGNALS: |
1088 | void textChannelAvailable(Tp::TextChannelPtr textChannel); |
1089 | |
1090 | === modified file 'handler/main.cpp' |
1091 | --- handler/main.cpp 2016-11-23 19:28:18 +0000 |
1092 | +++ handler/main.cpp 2016-12-17 13:33:18 +0000 |
1093 | @@ -30,6 +30,7 @@ |
1094 | #include <TelepathyQt/AbstractClient> |
1095 | #include <TelepathyQt/AccountManager> |
1096 | #include <TelepathyQt/Contact> |
1097 | +#include <telepathy-farstream/telepathy-farstream.h> |
1098 | |
1099 | int main(int argc, char **argv) |
1100 | { |
1101 | @@ -37,6 +38,7 @@ |
1102 | QCoreApplication::setApplicationName("telephony-service-handler"); |
1103 | |
1104 | Tp::registerTypes(); |
1105 | + gst_init(&argc, &argv); |
1106 | |
1107 | // check if there is already an instance of the handler running |
1108 | if (ApplicationUtils::checkApplicationRunning(TP_QT_IFACE_CLIENT + ".TelephonyServiceHandler")) { |
1109 | |
1110 | === modified file 'libtelephonyservice/accountentry.cpp' |
1111 | --- libtelephonyservice/accountentry.cpp 2016-10-05 19:15:39 +0000 |
1112 | +++ libtelephonyservice/accountentry.cpp 2016-12-17 13:33:18 +0000 |
1113 | @@ -28,6 +28,10 @@ |
1114 | |
1115 | Q_DECLARE_METATYPE(Tp::ConnectionPtr); |
1116 | |
1117 | +namespace C { |
1118 | +#include <libintl.h> |
1119 | +} |
1120 | + |
1121 | AccountEntry::AccountEntry(const Tp::AccountPtr &account, QObject *parent) : |
1122 | QObject(parent), mAccount(account), mReady(false), mProtocol(0) |
1123 | { |
1124 | @@ -51,10 +55,19 @@ |
1125 | |
1126 | bool AccountEntry::active() const |
1127 | { |
1128 | - return (!mAccount.isNull() && |
1129 | - !mAccount->connection().isNull() && |
1130 | - !mAccount->connection()->selfContact().isNull() && |
1131 | - mAccount->connection()->selfContact()->presence().type() != Tp::ConnectionPresenceTypeOffline); |
1132 | + if (mAccount.isNull() || mAccount->connection().isNull() || mAccount->connection()->status() != Tp::ConnectionStatusConnected) { |
1133 | + return false; |
1134 | + } |
1135 | + |
1136 | + // we have to check if the account supports simple presence. In case it does, we use the self contact presence to determine |
1137 | + // if this account is active. |
1138 | + if (mAccount->connection()->hasInterface(TP_QT_IFACE_CONNECTION_INTERFACE_SIMPLE_PRESENCE)) { |
1139 | + return (!mAccount->connection()->selfContact().isNull() && |
1140 | + mAccount->connection()->selfContact()->presence().type() != Tp::ConnectionPresenceTypeOffline); |
1141 | + } |
1142 | + |
1143 | + // if it doesn't support simple presence, we consider it online by having a connection in connected state |
1144 | + return true; |
1145 | } |
1146 | |
1147 | QString AccountEntry::displayName() const |
1148 | |
1149 | === modified file 'libtelephonyservice/callentry.cpp' |
1150 | --- libtelephonyservice/callentry.cpp 2015-06-09 21:59:29 +0000 |
1151 | +++ libtelephonyservice/callentry.cpp 2016-12-17 13:33:18 +0000 |
1152 | @@ -281,11 +281,15 @@ |
1153 | |
1154 | bool CallEntry::incoming() const |
1155 | { |
1156 | - if (!mAccount) { |
1157 | - return false; |
1158 | + bool isIncoming = !mChannel->isRequested(); |
1159 | + |
1160 | + if (mAccount && |
1161 | + !mChannel->initiatorContact().isNull() && |
1162 | + mChannel->initiatorContact() != mAccount->account()->connection()->selfContact()) { |
1163 | + isIncoming = true; |
1164 | } |
1165 | |
1166 | - return mChannel->initiatorContact() != mAccount->account()->connection()->selfContact(); |
1167 | + return isIncoming; |
1168 | } |
1169 | |
1170 | bool CallEntry::ringing() const |
1171 | @@ -428,10 +432,18 @@ |
1172 | { |
1173 | QDBusInterface *phoneAppHandler = TelepathyHelper::instance()->handlerInterface(); |
1174 | phoneAppHandler->call("SetMuted", mChannel->objectPath(), value); |
1175 | + |
1176 | + // FIXME: maybe we should retrieve the property from the handler instead of relying on telepathy |
1177 | + // for that, because on channels that are not using hardware streaming we handle the mute internally |
1178 | + // with no participation of the Telepathy mute interface |
1179 | + if (mChannel->handlerStreamingRequired()) { |
1180 | + onMutedChanged(value ? 1 : 0); |
1181 | + } |
1182 | } |
1183 | |
1184 | void CallEntry::onCallStateChanged(Tp::CallState state) |
1185 | { |
1186 | + qDebug() << __PRETTY_FUNCTION__ << state; |
1187 | // fetch the channel properties from the handler |
1188 | updateChannelProperties(); |
1189 | |
1190 | |
1191 | === modified file 'libtelephonyservice/callentry.h' |
1192 | --- libtelephonyservice/callentry.h 2015-03-04 18:02:13 +0000 |
1193 | +++ libtelephonyservice/callentry.h 2016-12-17 13:33:18 +0000 |
1194 | @@ -46,7 +46,7 @@ |
1195 | READ isVoicemail |
1196 | WRITE setVoicemail |
1197 | NOTIFY voicemailChanged) |
1198 | - Q_PROPERTY(AccountEntry *account READ account) |
1199 | + Q_PROPERTY(AccountEntry *account READ account CONSTANT) |
1200 | |
1201 | // FIXME: replace this by a more generic identifier to support accounts not based on phone numbers |
1202 | // this property is only filled for 1-1 calls |
1203 | |
1204 | === modified file 'libtelephonyservice/tonegenerator.cpp' |
1205 | --- libtelephonyservice/tonegenerator.cpp 2015-04-16 21:39:16 +0000 |
1206 | +++ libtelephonyservice/tonegenerator.cpp 2016-12-17 13:33:18 +0000 |
1207 | @@ -69,6 +69,7 @@ |
1208 | |
1209 | void ToneGenerator::playDTMFTone(uint key) |
1210 | { |
1211 | + qDebug() << __PRETTY_FUNCTION__ << key; |
1212 | if (key > 11) { |
1213 | qDebug() << "Invalid DTMF tone, ignore."; |
1214 | return; |
1215 | @@ -124,3 +125,13 @@ |
1216 | startEventTone(CALL_ENDED_TONE); |
1217 | QTimer::singleShot(2000, this, SLOT(stopTone())); |
1218 | } |
1219 | + |
1220 | +void ToneGenerator::playDialingTone() |
1221 | +{ |
1222 | + startEventTone(DIALING_TONE); |
1223 | +} |
1224 | + |
1225 | +void ToneGenerator::playRingingTone() |
1226 | +{ |
1227 | + startEventTone(RINGING_TONE); |
1228 | +} |
1229 | |
1230 | === modified file 'libtelephonyservice/tonegenerator.h' |
1231 | --- libtelephonyservice/tonegenerator.h 2015-04-16 21:39:16 +0000 |
1232 | +++ libtelephonyservice/tonegenerator.h 2016-12-17 13:33:18 +0000 |
1233 | @@ -31,6 +31,8 @@ |
1234 | static const int WAITING_PLAYBACK_DURATION = 8000; /* in milliseconds */ |
1235 | static const uint WAITING_TONE = 79; |
1236 | static const uint CALL_ENDED_TONE = 257; |
1237 | +static const uint DIALING_TONE = 66; |
1238 | +static const uint RINGING_TONE = 70; |
1239 | |
1240 | class ToneGenerator : public QObject |
1241 | { |
1242 | @@ -47,9 +49,11 @@ |
1243 | void playWaitingTone(); |
1244 | void stopWaitingTone(); |
1245 | void playCallEndedTone(); |
1246 | + void playDialingTone(); |
1247 | + void playRingingTone(); |
1248 | + void stopTone(); |
1249 | |
1250 | private Q_SLOTS: |
1251 | - void stopTone(); |
1252 | void stopDTMFTone(); |
1253 | bool startEventTone(uint key); |
1254 | |
1255 | |
1256 | === added file 'protocols/sip.protocol' |
1257 | --- protocols/sip.protocol 1970-01-01 00:00:00 +0000 |
1258 | +++ protocols/sip.protocol 2016-12-17 13:33:18 +0000 |
1259 | @@ -0,0 +1,7 @@ |
1260 | +[Protocol] |
1261 | +Name=sip |
1262 | +Features=voice |
1263 | +FallbackProtocol= |
1264 | +BackgroundImage=/usr/share/telephony-service/assets/message_watermark.png |
1265 | +ShowOnSelector=1 |
1266 | +ServiceDisplayName=SIP |
Replaced by: https:/ /code.launchpad .net/~phablet- team/telephony- service/ voip_support/ +merge/ 3137