diff options
author | Olivier CrĂȘte <olivier.crete@collabora.com> | 2012-02-23 19:24:24 -0500 |
---|---|---|
committer | Olivier CrĂȘte <olivier.crete@collabora.com> | 2012-02-23 19:24:59 -0500 |
commit | 86ded282b0416ab12583e1da0d21670aac632b6d (patch) | |
tree | 2c0300611f920cc683e212dcca11211adb06553f | |
parent | 92a846c32180c0ce51a4862b80b5159261d45b98 (diff) |
Make it possible to remove a content from a connected call
-rw-r--r-- | rakia/call-content.c | 24 | ||||
-rw-r--r-- | rakia/sip-media.c | 61 | ||||
-rw-r--r-- | rakia/sip-session.c | 13 | ||||
-rw-r--r-- | tests/twisted/Makefile.am | 4 | ||||
-rw-r--r-- | tests/twisted/voip/add-remove-content.py | 113 | ||||
-rw-r--r-- | tests/twisted/voip/calltest.py | 34 | ||||
-rw-r--r-- | tests/twisted/voip/direction-change.py | 28 | ||||
-rw-r--r-- | tests/twisted/voip/voip_test.py | 37 |
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>') |