summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorOlivier CrĂȘte <olivier.crete@collabora.com>2012-02-23 19:24:24 -0500
committerOlivier CrĂȘte <olivier.crete@collabora.com>2012-02-23 19:24:59 -0500
commit86ded282b0416ab12583e1da0d21670aac632b6d (patch)
tree2c0300611f920cc683e212dcca11211adb06553f
parent92a846c32180c0ce51a4862b80b5159261d45b98 (diff)
Make it possible to remove a content from a connected call
-rw-r--r--rakia/call-content.c24
-rw-r--r--rakia/sip-media.c61
-rw-r--r--rakia/sip-session.c13
-rw-r--r--tests/twisted/Makefile.am4
-rw-r--r--tests/twisted/voip/add-remove-content.py113
-rw-r--r--tests/twisted/voip/calltest.py34
-rw-r--r--tests/twisted/voip/direction-change.py28
-rw-r--r--tests/twisted/voip/voip_test.py37
8 files changed, 244 insertions, 70 deletions
diff --git a/rakia/call-content.c b/rakia/call-content.c
index 20a18ec..6031601 100644
--- a/rakia/call-content.c
+++ b/rakia/call-content.c
@@ -188,9 +188,16 @@ rakia_call_content_deinit (TpBaseCallContent *base)
{
RakiaCallContent *self = RAKIA_CALL_CONTENT (base);
RakiaCallContentPrivate *priv = self->priv;
+ RakiaSipSession *session;
+
+ session = rakia_sip_media_get_session (priv->media);
+
+ /* If the media was removed, it means it's by user request, so we must
+ * do a re-invite
+ */
+ if (rakia_sip_session_remove_media (session, priv->media, 0, NULL))
+ rakia_sip_session_media_changed (session);
- rakia_sip_session_remove_media (rakia_sip_media_get_session (priv->media),
- priv->media, 0, NULL );
tp_clear_object (&priv->stream);
tp_clear_object (&priv->channel);
@@ -371,9 +378,18 @@ rakia_call_content_add_stream (RakiaCallContent *self)
if (rakia_sip_media_get_requested_direction (priv->media) &
RAKIA_DIRECTION_SEND)
- local_sending_state = TP_SENDING_STATE_PENDING_SEND;
+ {
+ if (!tp_base_call_channel_is_accepted (
+ TP_BASE_CALL_CHANNEL (priv->channel)) &&
+ !tp_base_channel_is_requested (TP_BASE_CHANNEL (priv->channel)))
+ local_sending_state = TP_SENDING_STATE_PENDING_SEND;
+ else
+ local_sending_state = TP_SENDING_STATE_SENDING;
+ }
else
- local_sending_state = TP_SENDING_STATE_NONE;
+ {
+ local_sending_state = TP_SENDING_STATE_NONE;
+ }
if (rakia_sip_media_get_requested_direction (priv->media) &
diff --git a/rakia/sip-media.c b/rakia/sip-media.c
index 37cb0ad..2fe0cae 100644
--- a/rakia/sip-media.c
+++ b/rakia/sip-media.c
@@ -907,22 +907,30 @@ rakia_sip_media_set_remote_media (RakiaSipMedia *media,
/* Make sure we stop sending before we use the new set of codecs
* intended for the new connection */
if (codecs_changed)
- priv->push_candidates_on_new_codecs = TRUE;
+ {
+ priv->push_candidates_on_new_codecs = TRUE;
+ if (priv->remote_candidates != NULL)
+ {
+ g_ptr_array_unref (priv->remote_candidates);
+ priv->remote_candidates = NULL;
+ g_signal_emit (media, signals[SIG_REMOTE_CANDIDATES_UPDATED], 0);
+ }
+ }
else
- push_remote_candidates (media);
+ {
+ push_remote_candidates (media);
+ }
}
if (codecs_changed)
{
- if (!priv->codec_intersect_pending)
- {
- priv->codec_intersect_pending = TRUE;
- push_remote_codecs (media);
- }
+ if (authoritative)
+ priv->codec_intersect_pending = TRUE;
+
+ if (priv->remote_codec_offer == NULL)
+ push_remote_codecs (media);
else
- {
- priv->push_remote_codecs_pending = TRUE;
- }
+ priv->push_remote_codecs_pending = TRUE;
}
/* TODO: this will go to session change commit code */
@@ -972,19 +980,14 @@ rakia_sip_media_take_local_codecs (RakiaSipMedia *self, GPtrArray *local_codecs)
g_ptr_array_unref (priv->local_codecs);
priv->local_codecs = local_codecs;
- MEDIA_DEBUG (self, "New local codecs intersect_pending: %d "
- "push_candidates: %d candidates_prepared: %d",
- priv->codec_intersect_pending, priv->push_candidates_on_new_codecs,
- priv->local_candidates_prepared);
-
-
if (priv->push_remote_codecs_pending)
{
priv->push_remote_codecs_pending = FALSE;
push_remote_codecs (self);
}
- else if (priv->codec_intersect_pending)
+ else
{
+
if (priv->push_candidates_on_new_codecs)
{
/* Push the new candidates now that we have new codecs */
@@ -992,18 +995,22 @@ rakia_sip_media_take_local_codecs (RakiaSipMedia *self, GPtrArray *local_codecs)
push_remote_candidates (self);
}
- priv->codec_intersect_pending = FALSE;
- if (rakia_sip_media_is_ready (self))
+ if (priv->codec_intersect_pending)
{
- g_signal_emit (self, signals[SIG_LOCAL_NEGOTIATION_COMPLETE], 0,
- TRUE);
- g_ptr_array_unref (priv->remote_codec_offer);
- priv->remote_codec_offer = NULL;
+
+ priv->codec_intersect_pending = FALSE;
+ if (rakia_sip_media_is_ready (self))
+ {
+ g_signal_emit (self, signals[SIG_LOCAL_NEGOTIATION_COMPLETE], 0,
+ TRUE);
+ g_ptr_array_unref (priv->remote_codec_offer);
+ priv->remote_codec_offer = NULL;
+ }
+ }
+ else
+ {
+ rakia_sip_media_local_updated (self);
}
- }
- else
- {
- rakia_sip_media_local_updated (self);
}
}
diff --git a/rakia/sip-session.c b/rakia/sip-session.c
index 57d0045..44e8f4b 100644
--- a/rakia/sip-session.c
+++ b/rakia/sip-session.c
@@ -85,12 +85,12 @@ static const char *const session_states[NUM_RAKIA_SIP_SESSION_STATES] =
};
#define SESSION_DEBUG(session, format, ...) \
- rakia_log (DEBUG_FLAG, G_LOG_LEVEL_DEBUG, "session [%-17s]: " format, \
- session_states[(session)->priv->state],##__VA_ARGS__)
+ rakia_log (DEBUG_FLAG, G_LOG_LEVEL_DEBUG, "%s [%-17s]: " format, \
+ G_STRFUNC, session_states[(session)->priv->state],##__VA_ARGS__)
#define SESSION_MESSAGE(session, format, ...) \
- rakia_log (DEBUG_FLAG, G_LOG_LEVEL_MESSAGE, "session [%-17s]: " format, \
- session_states[(session)->priv->state],##__VA_ARGS__)
+ rakia_log (DEBUG_FLAG, G_LOG_LEVEL_MESSAGE, "%s [%-17s]: " format, \
+ G_STRFUNC, session_states[(session)->priv->state],##__VA_ARGS__)
#else /* !ENABLE_DEBUG */
@@ -845,6 +845,9 @@ rakia_sip_session_media_changed (RakiaSipSession *self)
else
priv->pending_offer = TRUE;
break;
+ case RAKIA_SIP_SESSION_STATE_ENDED:
+ /* We've already ended the call, ignore any change request */
+ break;
default:
g_assert_not_reached();
}
@@ -1104,6 +1107,7 @@ priv_session_invite (RakiaSipSession *session, gboolean reinvite)
SOATAG_USER_SDP_STR(priv->local_sdp),
SOATAG_RTP_SORT(SOA_RTP_SORT_REMOTE),
SOATAG_RTP_SELECT(SOA_RTP_SELECT_ALL),
+ SOATAG_ORDERED_USER(1),
NUTAG_AUTOANSWER(0),
TAG_IF(reinvite,
NUTAG_INVITE_TIMER (RAKIA_REINVITE_TIMEOUT)),
@@ -1137,7 +1141,6 @@ priv_session_respond (RakiaSipSession *session)
priv->local_sdp = g_string_free (user_sdp, FALSE);
}
-
/* We need to be prepared to receive media right after the
* answer is sent, so we must set the streams to playing */
g_signal_emit (session, signals[SIG_START_RECEIVING], 0);
diff --git a/tests/twisted/Makefile.am b/tests/twisted/Makefile.am
index 870e1b3..a0b30d6 100644
--- a/tests/twisted/Makefile.am
+++ b/tests/twisted/Makefile.am
@@ -13,8 +13,8 @@ TWISTED_TESTS = \
voip/calltest.py \
voip/ringing-queued.py \
voip/requestable-classes.py \
- voip/direction-change.py
- voip/add-remote-content.py \
+ voip/direction-change.py \
+ voip/add-remove-content.py \
$(NULL)
TESTS =
diff --git a/tests/twisted/voip/add-remove-content.py b/tests/twisted/voip/add-remove-content.py
index 6921997..90c4246 100644
--- a/tests/twisted/voip/add-remove-content.py
+++ b/tests/twisted/voip/add-remove-content.py
@@ -1,5 +1,6 @@
import calltest
import constants as cs
+import re
from servicetest import (
EventPattern, call_async,
assertEquals, assertNotEquals, assertContains, assertLength,
@@ -11,11 +12,63 @@ class AddRemoveContent(calltest.CallTest):
def __init__(self, *params):
calltest.CallTest.__init__(self, *params)
+ def add_content_succesful(self):
+ o = self.chan.Call1.AddContent("new audio", cs.MEDIA_STREAM_TYPE_AUDIO,
+ cs.MEDIA_STREAM_DIRECTION_BIDIRECTIONAL)
- def during_call(self):
- content = self.contents[0]
+ self.q.expect('dbus-signal', signal='ContentAdded',
+ args=[o], path=self.chan.__dbus_object_path__)
+
+ content = self.add_content(o, initial=False, incoming=False)
+
+ self.add_candidates(content.stream)
+ md_path, _ = content.Get(cs.CALL_CONTENT_IFACE_MEDIA,
+ 'MediaDescriptionOffer')
+ md = self.bus.get_object (self.conn.bus_name, md_path)
+ md.Accept(self.context.get_audio_md_dbus(self.remote_handle))
+ self.q.expect_many(
+ EventPattern('dbus-signal', signal='MediaDescriptionOfferDone'),
+ EventPattern('dbus-signal', signal='LocalMediaDescriptionChanged'),
+ EventPattern('dbus-signal', signal='RemoteMediaDescriptionsChanged'))
+
+ content.stream.Media.CompleteReceivingStateChange(
+ cs.CALL_STREAM_FLOW_STATE_STARTED)
+
+ reinvite_event, _ = self.q.expect_many(
+ EventPattern('sip-invite'),
+ EventPattern('dbus-signal', signal='ReceivingStateChanged',
+ args=[cs.CALL_STREAM_FLOW_STATE_STARTED],
+ path=content.stream.__dbus_object_path__))
+
+ self.add_to_medias('audio')
+
+ self.context.check_call_sdp(reinvite_event.sip_message.body,
+ self.medias)
+
+ self.context.accept(reinvite_event.sip_message)
+
+ ack_cseq = "%s ACK" % reinvite_event.cseq.split()[0]
+
+ self.q.expect_many(
+ EventPattern('sip-ack', cseq=ack_cseq),
+ EventPattern('dbus-signal', signal='SendingStateChanged',
+ args=[cs.CALL_STREAM_FLOW_STATE_PENDING_START],
+ path=content.stream.__dbus_object_path__))
+
+
+ content.stream.Media.CompleteSendingStateChange(
+ cs.CALL_STREAM_FLOW_STATE_STARTED)
+
+ self.q.expect('dbus-signal', signal='SendingStateChanged',
+ args=[cs.CALL_STREAM_FLOW_STATE_STARTED],
+ path=content.stream.__dbus_object_path__)
+
+ return content
+
+
+ def add_content_rejected(self):
o = self.chan.Call1.AddContent("new audio", cs.MEDIA_STREAM_TYPE_AUDIO,
cs.MEDIA_STREAM_DIRECTION_BIDIRECTIONAL)
@@ -45,12 +98,64 @@ class AddRemoveContent(calltest.CallTest):
args=[cs.CALL_STREAM_FLOW_STATE_STARTED],
path=content.stream.__dbus_object_path__))
- medias = self.medias + [('audio', None)]
+
+ self.context.check_call_sdp(reinvite_event.sip_message.body,
+ self.medias + [('audio', None, None)])
+ res = re.match('(.*)(m=.*)', reinvite_event.sip_message.body,
+ re.MULTILINE | re.DOTALL)
+
+ body = res.group(1) + 'm=audio 0 RTP/AVP 0'
+
+ self.add_to_medias(None)
+
+ self.context.accept(reinvite_event.sip_message, body)
+
+ ack_cseq = "%s ACK" % reinvite_event.cseq.split()[0]
+
+ o = self.q.expect_many(
+ EventPattern('sip-ack', cseq=ack_cseq),
+ EventPattern('dbus-signal', signal='ContentRemoved',
+ path=self.chan_path))
+
+ assertEquals(content.__dbus_object_path__, o[1].args[0])
+ assertEquals(self.remote_handle, o[1].args[1][0])
+
+ self.contents.remove(content)
+
+
+ def remove_content_successful(self, x):
+ content = self.contents[x]
+ self.contents.remove(content)
+ content.Remove()
+
+ reinvite_event, content_removed = self.q.expect_many(
+ EventPattern('sip-invite'),
+ EventPattern('dbus-signal', signal='ContentRemoved',
+ path=self.chan.__dbus_object_path__))
+ assertEquals(content.__dbus_object_path__, content_removed.args[0])
+ assertEquals(self.self_handle, content_removed.args[1][0])
+ assertEquals(cs.CALL_SCR_USER_REQUESTED, content_removed.args[1][1])
+
+ self.medias[x] = (None, None)
self.context.check_call_sdp(reinvite_event.sip_message.body,
- medias)
+ self.medias)
+
self.context.accept(reinvite_event.sip_message)
+ ack_cseq = "%s ACK" % reinvite_event.cseq.split()[0]
+
+ self.q.expect('sip-ack', cseq=ack_cseq)
+
+
+ def during_call(self):
+ self.add_content_succesful()
+ self.add_content_succesful()
+ self.add_content_rejected()
+ self.add_content_succesful()
+ self.remove_content_successful(0)
+ self.add_content_succesful()
+
return calltest.CallTest.during_call(self)
diff --git a/tests/twisted/voip/calltest.py b/tests/twisted/voip/calltest.py
index 1d130d2..07f6115 100644
--- a/tests/twisted/voip/calltest.py
+++ b/tests/twisted/voip/calltest.py
@@ -40,11 +40,18 @@ class CallTest:
self.medias = []
if self.initial_audio_content_name:
- self.medias += [('audio', None)]
+ self.add_to_medias('audio')
if self.initial_video_content_name:
- self.medias += [('video', None)]
+ self.add_to_medias('video')
+ def add_to_medias(self, mediatype, direction=None):
+ for i in range(0, len(self.medias)):
+ if self.medias[i][1] == 0:
+ self.medias[i] = (mediatype, direction)
+ return
+ self.medias += [(mediatype, direction)]
+
def connect(self):
self.conn.Connect()
self.q.expect('dbus-signal', signal='StatusChanged', args=[0, 1])
@@ -104,7 +111,7 @@ class CallTest:
endpoint.Get(cs.CALL_STREAM_ENDPOINT, 'EndpointState'))
- def __add_stream (self, content, stream_path, initial):
+ def __add_stream (self, content, stream_path, initial, incoming):
tmpstream = self.bus.get_object (self.conn.bus_name, stream_path)
content.stream = ProxyWrapper (tmpstream, cs.CALL_STREAM,
@@ -112,8 +119,22 @@ class CallTest:
stream_props = content.stream.Properties.GetAll(cs.CALL_STREAM)
assertEquals(True, stream_props['CanRequestReceiving'])
- assertEquals(cs.CALL_SENDING_STATE_PENDING_SEND,
- stream_props['LocalSendingState'])
+ if incoming:
+ assertEquals(cs.CALL_SENDING_STATE_PENDING_SEND,
+ stream_props['LocalSendingState'])
+ else:
+ assertEquals(cs.CALL_SENDING_STATE_SENDING,
+ stream_props['LocalSendingState'])
+
+ if initial or not incoming:
+ assertEquals(
+ {self.remote_handle: cs.CALL_SENDING_STATE_PENDING_SEND},
+ stream_props['RemoteMembers'])
+ else:
+ assertEquals(
+ {self.remote_handle: cs.CALL_SENDING_STATE_SENDING},
+ stream_props['RemoteMembers'])
+
smedia_props = content.stream.Properties.GetAll(
cs.CALL_STREAM_IFACE_MEDIA)
@@ -183,7 +204,8 @@ class CallTest:
self.contents.append(content)
- self.__add_stream(content, content_props['Streams'][0], initial)
+ self.__add_stream(content, content_props['Streams'][0], initial,
+ incoming)
if incoming:
md = self.bus.get_object (self.conn.bus_name,
diff --git a/tests/twisted/voip/direction-change.py b/tests/twisted/voip/direction-change.py
index 8dd57cf..e1f0acc 100644
--- a/tests/twisted/voip/direction-change.py
+++ b/tests/twisted/voip/direction-change.py
@@ -104,18 +104,18 @@ class DirectionChange(calltest.CallTest):
self.q.forbid_events(lss_event)
self.q.forbid_events(direction_events)
- self.context.reinvite([('audio','sendonly')])
+ self.context.reinvite([('audio', None, 'sendonly')])
acc = self.q.expect('sip-response', call_id=self.context.call_id,
code=200)
self.context.check_call_sdp(acc.sip_message.body,
- [('audio','recvonly')])
+ [('audio', None, 'recvonly')])
self.context.ack(acc.sip_message)
self.q.unforbid_events(lss_event)
- self.context.reinvite([('audio','')])
+ self.context.reinvite([('audio',None, None)])
acc, lss = self.q.expect_many(
EventPattern('sip-response', call_id=self.context.call_id,
@@ -124,7 +124,7 @@ class DirectionChange(calltest.CallTest):
assertEquals(cs.CALL_SENDING_STATE_PENDING_SEND, lss.args[0])
assertEquals(self.remote_handle, lss.args[1][0])
self.context.check_call_sdp(acc.sip_message.body,
- [('audio','recvonly')])
+ [('audio', None, 'recvonly')])
assertEquals(cs.CALL_STREAM_FLOW_STATE_STOPPED,
content.stream.Properties.Get(cs.CALL_STREAM_IFACE_MEDIA,
@@ -159,7 +159,7 @@ class DirectionChange(calltest.CallTest):
self.context.check_call_sdp(reinvite_event.sip_message.body,
- [('audio','sendonly')])
+ [('audio', None, 'sendonly')])
if self.sending:
body = reinvite_event.sip_message.body.replace('sendonly',
'sendrecv')
@@ -247,7 +247,7 @@ class DirectionChange(calltest.CallTest):
reinvite_event = o[2]
self.context.check_call_sdp(reinvite_event.sip_message.body,
- [('audio','sendonly')])
+ [('audio', None, 'sendonly')])
body = reinvite_event.sip_message.body.replace(
'sendonly', self.sending and 'recvonly' or 'inactive')
@@ -365,7 +365,16 @@ class DirectionChange(calltest.CallTest):
ack_cseq = "%s ACK" % reinvite_event.cseq.split()[0]
self.q.expect('sip-ack', cseq=ack_cseq)
-
+
+ def hold(self):
+ pass
+
+ def unhold(self):
+ pass
+
+ def hold_unhold(self):
+ self.hold()
+ self.unhold()
def during_call(self):
content = self.contents[0]
@@ -384,6 +393,11 @@ class DirectionChange(calltest.CallTest):
self.reject_start_receiving(content)
self.q.unforbid_events(remote_hold_event)
+
+ self.hold_unhold()
+
+
+
return calltest.CallTest.during_call(self)
diff --git a/tests/twisted/voip/voip_test.py b/tests/twisted/voip/voip_test.py
index 8ad3cae..99d95ad 100644
--- a/tests/twisted/voip/voip_test.py
+++ b/tests/twisted/voip/voip_test.py
@@ -72,7 +72,7 @@ class VoipTestContext(object):
def get_remote_candidates_dbus(self):
return dbus.Array(self.remote_candidates, signature='(usua{sv})')
-
+
def get_call_sdp(self, medias):
(component, ip, port, info) = self.remote_candidates[0]
codec_id_list = []
@@ -88,15 +88,19 @@ class VoipTestContext(object):
's=-\r\n' + \
't=0 0\r\n'
for m in medias:
- sdp_string += 'm=' + m[0] + ' %(port)s RTP/AVP 3 8 0\r\n' \
- 'c=IN IP4 %(ip)s\r\n' \
- '%(codecs)s\r\n'
- if m[1]:
- sdp_string += 'a=' + m[1] + '\r\n'
+ if m[0]:
+ sdp_string += 'm=' + m[0] + ' %(port)s RTP/AVP 3 8 0\r\n' \
+ 'c=IN IP4 %(ip)s\r\n' \
+ '%(codecs)s\r\n'
+ if m[1]:
+ sdp_string += 'a=' + m[1] + '\r\n'
+ else:
+ sdp_string += 'm=audio 0 RTP/AVP\r\n'
+
return sdp_string % locals()
- def check_call_sdp(self, sdp_string, medias=[('audio',None)]):
+ def check_call_sdp(self, sdp_string, medias=[('audio', None)]):
codec_id_list = []
for name, codec_id, rate, _misc in self.audio_codecs:
assertContains (self._aline_template % locals(), sdp_string)
@@ -106,11 +110,14 @@ class VoipTestContext(object):
(component, ip, port, info) = self.remote_candidates[0]
pattern = '.*'
for m in medias:
- mediatype = m[0]
- pattern += self._mline_template % locals()
- pattern += '.*'
- if m[1]:
- pattern += 'a=' + m[1] + '.*'
+ if m[0]:
+ mediatype = m[0]
+ pattern += self._mline_template % locals()
+ pattern += '.*'
+ if m[1]:
+ pattern += 'a=' + m[1] + '.*'
+ else:
+ pattern += 'm=audio 0 RTP/AVP .*'
assert re.search(pattern, sdp_string, re.MULTILINE | re.DOTALL)
def send_message(self, message_type, body='', to_=None, from_=None,
@@ -159,12 +166,12 @@ class VoipTestContext(object):
cseq = '%s ACK' % ok_message.headers['cseq'][0].split()[0]
self.send_message('ACK', call_id=self.call_id, cseq=cseq)
- def reinvite(self, medias=[('audio',None)]):
+ def reinvite(self, medias=[('audio', None)]):
body = self.get_call_sdp(medias)
return self.send_message('INVITE', body, content_type='application/sdp',
supported='timer, 100rel', call_id=self.call_id)
- def incoming_call(self, medias=[('audio',None)]):
+ def incoming_call(self, medias=[('audio', None)]):
self.call_id = uuid.uuid4().hex
body = self.get_call_sdp(medias)
return self.send_message('INVITE', body, content_type='application/sdp',
@@ -172,7 +179,7 @@ class VoipTestContext(object):
def incoming_call_from_self(self):
self.call_id = uuid.uuid4().hex
- body = self.get_call_sdp([('audio',None)])
+ body = self.get_call_sdp([('audio', None)])
return self.send_message('INVITE', body, content_type='application/sdp',
supported='timer, 100rel', call_id=self.call_id,
from_='<sip:testacc@127.0.0.1>')