bayonne-devel
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

Re: [Bayonne-devel] - SIP fast answer


From: David Sugar
Subject: Re: [Bayonne-devel] - SIP fast answer
Date: Thu, 24 Aug 2006 13:46:50 -0700
User-agent: Thunderbird 1.5.0.5 (X11/20060808)

There are many different sip answering "standards" and scenarios,
including the 3 SDP handshake through invite, prack, and then update,
that is used in 3gpp/IMS :).  Others include one way audio at initial
call establishment followed by a late "200" answer.  I choose the
simplest that worked reliably with the things I tested.  In the case of
.28 I am adding extra support to allow selection of any of these in the
registry answer= option, including the ims one.  In the release you
have, you may try setting answer="180,200", which, as I recall issues a
180 ringing, followed immediately by a 200 with sdp (you will get a real
short ring blip at the start of the call).

I am basing this on the fact that your client is requesting the call to
be closed, which to me suggests it did not like what it saw in the
initial answer sequence, rather than a rtp issue.  But it is simply an
impression. SIP is a simple idea and standard in principle, but can be a
nightmare in actual implementation today.

Julien Chavanton wrote:
> Thanks, your first impression is that the problem is related to SIP
> answer.
> 
> I was looking at ccRTP since I was thinking the sip session was started
> and closed correctly.
> 
> 
> 
> -----Original Message-----
> From: David Sugar [mailto:address@hidden 
> Sent: August 23, 2006 2:49 PM
> To: Julien Chavanton
> Cc: address@hidden
> Subject: Re: [Bayonne-devel] - SIP fast answer
> 
> By default, to an invite, Bayonne responds immediately with a 200 OK
> with a SDP.  This is known as the "fast" SIP answering mode, and has
> worked with the softphone clients and service providers I tested with.
> 
> There are other call setup scenarios, including delayed answer ones,
> where one can potentially send a 100 trying, perhaps a 183 call
> proceeding (with or without SDP depending on who generates ringback/if
> announcements will be used) or 180 call ringing, and one could then
> immediately send or later delay the 200 OK answer with the final SDP.  I
> have been doing some work to support various immediate and delayed
> answer scenarios in .28, as well as enabling one to explicitly state the
> answering sequence to use in the answer= option of the register command.
> 
> It would help to know what sequence of replies to invite the
> softphone(s) you are testing with are expecting given the range of
> possibilities.
> 
> Julien Chavanton wrote:
>> Samething with eStara SoftPhone:
>>
>> sip: event 0005=EXOSIP_CALL_INVITE, cid=7, did=8, rid=0, instance=4
>> sip/3: state=idle, event=201, seq=1
>> sip/3: state=pickup, event=100, seq=1
>> sip/3: going active; associated with 202
>> sip: event 000f=EXOSIP_CALL_ACK, cid=7, did=8, rid=0, instance=4
>> sip/3: state=pickup, event=503, seq=2
>> sip/3: state=run, event=100, seq=2
>> sip/3: BREAK1 dtmf_say
>> sip/3: BREAK2 dtmf_say
>> sip: event 0012=EXOSIP_CALL_MESSAGE_NEW, cid=7, did=8, rid=0,
> instance=4
>> sip: event 0019=EXOSIP_CALL_CLOSED, cid=7, did=8, rid=0, instance=5
>> sip/3: closing call
>> sip/3: state=run, event=209, seq=3
>>
>>
>>
>> -----Original Message-----
>> From:
> address@hidden
> [mailto:address@hidden
>> g] On Behalf Of Julien Chavanton
>> Sent: August 22, 2006 10:21 AM
>> To: David Sugar
>> Cc: address@hidden
>> Subject: RE: [Bayonne-devel] Bayonne2, exosip2 2.2.3pl1?
>>
>> I downloaded and installed "bayonne2-1.5.27"
>>
>>
>> sip: event 0005=EXOSIP_CALL_INVITE, cid=1, did=2, rid=0, instance=4
>> sip/0: state=idle, event=201, seq=1
>> sip/0: state=pickup, event=100, seq=1
>> sip/0: going active; associated with 202
>> sip: event 000f=EXOSIP_CALL_ACK, cid=1, did=2, rid=0, instance=4
>> sip/0: state=pickup, event=503, seq=2
>> sip/0: state=run, event=100, seq=2
>> sip/0: BREAK1 dtmf_say
>> sip/0: BREAK2 dtmf_say
>> sip: event 0012=EXOSIP_CALL_MESSAGE_NEW, cid=1, did=2, rid=0,
> instance=3
>> sip: event 0019=EXOSIP_CALL_CLOSED, cid=1, did=2, rid=0, instance=4
>> sip/0: closing call
>> sip/0: state=run, event=209, seq=3
>>
>>
>>
>>
>> Could we make a special debug flag for sip when sarting bayonne:
>>
>> Then we could use something like this to have the event description
>> instead of the hex value this is helping a lot when we debug.
>>
>> Here I have all the event from the latest doc so we can see
> unsolicited
>> event if there is any.
>>
>> /*
>>                 slog.debug("sip: event %04x; cid=%d, did=%d, rid=%d,
>> instance=%d",
>>                         sevent->type, sevent->cid, sevent->did,
>> sevent->rid, thread);
>> */
>>
>>   char event_str[40];
>>   strcpy(event_str,"none");
>>   switch(sevent->type)
>>   {
>>   case EXOSIP_REGISTRATION_NEW:
>> strcpy(event_str,"EXOSIP_REGISTRATION_NEW"); break;
>>   case EXOSIP_REGISTRATION_SUCCESS:
>> strcpy(event_str,"EXOSIP_REGISTRATION_SUCCESS"); break;
>>   case EXOSIP_REGISTRATION_FAILURE:
>> strcpy(event_str,"EXOSIP_REGISTRATION_FAILURE"); break;
>>   case EXOSIP_REGISTRATION_REFRESHED:
>> strcpy(event_str,"EXOSIP_REGISTRATION_REFRESHED"); break;
>>   case EXOSIP_REGISTRATION_TERMINATED:
>> strcpy(event_str,"EXOSIP_REGISTRATION_TERMINATED"); break;
>>   case EXOSIP_CALL_INVITE: strcpy(event_str,"EXOSIP_CALL_INVITE");
>> break;
>>   case EXOSIP_CALL_REINVITE: strcpy(event_str,"EXOSIP_CALL_REINVITE");
>> break;
>>   case EXOSIP_CALL_NOANSWER: strcpy(event_str,"EXOSIP_CALL_NOANSWER");
>> break;
>>   case EXOSIP_CALL_PROCEEDING:
>> strcpy(event_str,"EXOSIP_CALL_PROCEEDING"); break;
>>   case EXOSIP_CALL_RINGING: strcpy(event_str," EXOSIP_CALL_RINGING");
>> break;
>>   case EXOSIP_CALL_ANSWERED: strcpy(event_str,"EXOSIP_CALL_ANSWERED");
>> break;
>>   case EXOSIP_CALL_REDIRECTED:
>> strcpy(event_str,"EXOSIP_CALL_REDIRECTED"); break;
>>   case EXOSIP_CALL_REQUESTFAILURE:
>> strcpy(event_str,"EXOSIP_CALL_REQUESTFAILURE"); break;
>>   case EXOSIP_CALL_SERVERFAILURE:
>> strcpy(event_str,"EXOSIP_CALL_SERVERFAILURE"); break;
>>   case EXOSIP_CALL_GLOBALFAILURE:
>> strcpy(event_str,"EXOSIP_CALL_GLOBALFAILURE"); break;
>>   case EXOSIP_CALL_ACK: strcpy(event_str,"EXOSIP_CALL_ACK"); break;
>>   case EXOSIP_CALL_CANCELLED:
> strcpy(event_str,"EXOSIP_CALL_CANCELLED");
>> break;
>>   case EXOSIP_CALL_TIMEOUT: strcpy(event_str,"EXOSIP_CALL_TIMEOUT");
>> break;
>>   case EXOSIP_CALL_MESSAGE_NEW:
>> strcpy(event_str,"EXOSIP_CALL_MESSAGE_NEW"); break;
>>   case EXOSIP_CALL_MESSAGE_PROCEEDING:
>> strcpy(event_str,"EXOSIP_REGISTRATION_NEW"); break;
>>   case EXOSIP_CALL_MESSAGE_ANSWERED:
>> strcpy(event_str,"EXOSIP_CALL_MESSAGE_ANSWERED"); break;
>>   case EXOSIP_CALL_MESSAGE_REDIRECTED:
>> strcpy(event_str,"EXOSIP_CALL_MESSAGE_REDIRECTED"); break;
>>   case EXOSIP_CALL_MESSAGE_REQUESTFAILURE:
>> strcpy(event_str,"EXOSIP_CALL_MESSAGE_REQUESTFAILURE"); break;
>>   case EXOSIP_CALL_MESSAGE_SERVERFAILURE:
>> strcpy(event_str,"EXOSIP_CALL_MESSAGE_SERVERFAILURE"); break;
>>   case EXOSIP_CALL_MESSAGE_GLOBALFAILURE:
>> strcpy(event_str,"EXOSIP_CALL_MESSAGE_GLOBALFAILURE"); break;
>>   case EXOSIP_CALL_CLOSED: strcpy(event_str,"EXOSIP_CALL_CLOSED");
>> break;
>>   case EXOSIP_CALL_RELEASED: strcpy(event_str,"EXOSIP_CALL_RELEASED");
>> break;
>>   case EXOSIP_MESSAGE_NEW: strcpy(event_str,"EXOSIP_MESSAGE_NEW");
>> break;
>>   case EXOSIP_MESSAGE_PROCEEDING:
>> strcpy(event_str,"EXOSIP_MESSAGE_PROCEEDING"); break;
>>   case EXOSIP_MESSAGE_ANSWERED:
>> strcpy(event_str,"EXOSIP_MESSAGE_ANSWERED"); break;
>>   case EXOSIP_MESSAGE_REDIRECTED:
>> strcpy(event_str,"EXOSIP_MESSAGE_REDIRECTED"); break;
>>   case EXOSIP_MESSAGE_REQUESTFAILURE:
>> strcpy(event_str,"EXOSIP_MESSAGE_REQUESTFAILURE"); break;
>>   case EXOSIP_MESSAGE_SERVERFAILURE:
>> strcpy(event_str,"EXOSIP_MESSAGE_SERVERFAILURE"); break;
>>   case EXOSIP_MESSAGE_GLOBALFAILURE:
>> strcpy(event_str,"EXOSIP_MESSAGE_GLOBALFAILURE"); break;
>>   case EXOSIP_SUBSCRIPTION_UPDATE:
>> strcpy(event_str,"EXOSIP_SUBSCRIPTION_UPDATE"); break;
>>   case EXOSIP_SUBSCRIPTION_CLOSED:
>> strcpy(event_str,"EXOSIP_SUBSCRIPTION_CLOSED"); break;
>>   case EXOSIP_SUBSCRIPTION_NOANSWER:
>> strcpy(event_str,"EXOSIP_SUBSCRIPTION_NOANSWER"); break;
>>   case EXOSIP_SUBSCRIPTION_PROCEEDING:
>> strcpy(event_str,"EXOSIP_SUBSCRIPTION_PROCEEDING"); break;
>>   case EXOSIP_SUBSCRIPTION_ANSWERED:
>> strcpy(event_str,"EXOSIP_SUBSCRIPTION_ANSWERED"); break;
>>   case EXOSIP_SUBSCRIPTION_REDIRECTED:
>> strcpy(event_str,"EXOSIP_SUBSCRIPTION_REDIRECTED"); break;
>>   case EXOSIP_SUBSCRIPTION_REQUESTFAILURE:
>> strcpy(event_str,"EXOSIP_SUBSCRIPTION_REQUESTFAILURE"); break;
>>   case EXOSIP_SUBSCRIPTION_SERVERFAILURE:
>> strcpy(event_str,"EXOSIP_SUBSCRIPTION_SERVERFAILURE"); break;
>>   case EXOSIP_SUBSCRIPTION_GLOBALFAILURE:
>> strcpy(event_str,"EXOSIP_SUBSCRIPTION_GLOBALFAILURE"); break;
>>   case EXOSIP_SUBSCRIPTION_NOTIFY:
>> strcpy(event_str,"EXOSIP_SUBSCRIPTION_NOTIFY"); break;
>>   case EXOSIP_SUBSCRIPTION_RELEASED:
>> strcpy(event_str,"EXOSIP_SUBSCRIPTION_RELEASED"); break;
>>   case EXOSIP_IN_SUBSCRIPTION_NEW:
>> strcpy(event_str,"EXOSIP_IN_SUBSCRIPTION_NEW"); break;
>>   case EXOSIP_IN_SUBSCRIPTION_RELEASED:
>> strcpy(event_str,"EXOSIP_IN_SUBSCRIPTION_RELEASED"); break;
>>   case EXOSIP_NOTIFICATION_NOANSWER:
>> strcpy(event_str,"EXOSIP_NOTIFICATION_NOANSWER"); break;
>>   case EXOSIP_NOTIFICATION_PROCEEDING:
>> strcpy(event_str,"EXOSIP_NOTIFICATION_PROCEEDING"); break;
>>   case EXOSIP_NOTIFICATION_ANSWERED:
>> strcpy(event_str,"EXOSIP_NOTIFICATION_ANSWERED"); break;
>>   case EXOSIP_NOTIFICATION_REDIRECTED:
>> strcpy(event_str,"EXOSIP_NOTIFICATION_REDIRECTED"); break;
>>   case EXOSIP_NOTIFICATION_REQUESTFAILURE:
>> strcpy(event_str,"EXOSIP_NOTIFICATION_REQUESTFAILURE"); break;
>>   case EXOSIP_NOTIFICATION_SERVERFAILURE:
>> strcpy(event_str,"EXOSIP_NOTIFICATION_SERVERFAILURE"); break;
>>   case EXOSIP_NOTIFICATION_GLOBALFAILURE:
>> strcpy(event_str,"EXOSIP_NOTIFICATION_GLOBALFAILURE"); break;
>>   case EXOSIP_EVENT_COUNT: strcpy(event_str,"EXOSIP_EVENT_COUNT");
>> break;
>>   }
>>   slog.debug("sip: event %04x=%s, cid=%d, did=%d, rid=%d,
> instance=%d",
>>           sevent->type, event_str, sevent->cid, sevent->did,
>> sevent->rid, thread);
>>
>>
>>
>>
>>
>>
>>
>> -----Original Message-----
>> From: David Sugar [mailto:address@hidden 
>> Sent: August 21, 2006 6:37 PM
>> To: Julien Chavanton
>> Cc: Julien Chavanton; address@hidden
>> Subject: Re: [Bayonne-devel] Bayonne2, exosip2 2.2.3pl1?
>>
>> I found a typo in 2833 sdp negotiation which I fixed in .27, but I am
>> not sure if it is related to this.  When I get back on the east coast
> I
>> will try that client out.
>>
>> Julien Chavanton wrote:
>>> libeXosip2-2.2.3pl1
>>>
>>> I am still testing with XTen the call does not answer?
>>>
>>>
>>> sip: event 001b; cid=0, did=0, rid=0, instance=1
>>> EXOSIP_MESSAGE_NEW
>>> sip: out of dialog msg BYE
>>> sip: event 0005; cid=1, did=2, rid=0, instance=1
>>> EXOSIP_CALL_INVITE
>>> sip/0: state=idle, event=201, seq=1
>>> sip/0: state=pickup, event=100, seq=1
>>> sip/0: going active; associated with 202
>>> sip: event 000f; cid=1, did=2, rid=0, instance=1
>>> EXOSIP_CALL_ACK
>>> sip/0: state=pickup, event=503, seq=2
>>> sip/0: state=run, event=100, seq=2
>>> sip/0: BREAK1 dtmf_say
>>> sip/0: BREAK2 dtmf_say
>>> sip: event 0012; cid=1, did=2, rid=0, instance=3
>>> EXOSIP_CALL_MESSAGE_NEW
>>> sip: event 0019; cid=1, did=2, rid=0, instance=1
>>> EXOSIP_CALL_CLOSED
>>> sip/0: closing call
>>> sip/0: state=run, event=209, seq=3
>>> driver(s) stopping...
>>> sip/0: state=run, event=208, seq=4
>>> sip/0: state=hangup, event=100, seq=4
>>>
>>>
>>> -----Original Message-----
>>> From:
>> address@hidden
>>
> [mailto:address@hidden
>>> g] On Behalf Of David Sugar
>>> Sent: August 12, 2006 8:31 AM
>>> To: Julien Chavanton
>>> Cc: address@hidden
>>> Subject: Re: [Bayonne-devel] Bayonne2, exosip2 2.2.3pl1?
>>>
>>> Which version of eXosip2 are you using?  We had to create a special
>>> patched version of 2.2.3 (which later became a feature in 3.0.0,
>> though
>>> I have not migrated to it yet) because the stack would send a
>> 101/dialog
>>> entry during call setup, and this seemed to crash at least some
>>> softphone clients (cisco and snom in particular).  Besides snom, I
>> have
>>> mostly tested client connections  (inbound and out) with linphone
>> (which
>>> had the slow rtp session start and uses older exosip code) and with
>>> Twinkle, which also has it's own SIP implementation.  I have not
>> tested
>>> with x-ten yet, however.
>>>
>>> On the service provider/service source side, I have done most of my
>>> testing with another Bayonne server, with Broadvoice, and against an
>>> Asterisk server.  Broadvoice in particular helped a lot to get the
>>> authentication digest/realm code and the adpcm (g721/g726-32)
> encoding
>>> working correctly.
>>>
>>> Julien Chavanton wrote:
>>>> Better but still not working since the script exute only to log
>>> command and
>>>> does nothing more?
>>>>
>>>>
>>>> bool Session::enterPickup(Event *event)
>>>> {
>>>>        if(event->id == ENTER_STATE)
>>>>        {
>>>>                offhook = true;
>>>>                startRTP();
>>>>                startTimer(driver->getPickupTimer());
>>>>                return true;
>>>>        }
>>>>        else if(event->id == CALL_ACCEPTED)
>>>>        {
>>>>                setRunning();
>>>>                // startTimer(Driver::sip.accept_timer);
>>>>                return true;
>>>>        }
>>>>
>>>>        return false;
>>>> }
>>>>
>>>>
>>>>
>>>>
> ------------------------------------------------------------------------
>>> -----------
>>>> register.sip proxy=192.168.0.156 uri=sip:address@hidden
>>> userid=202
>>>> secret=202 timeout=3600
>>>>
>>>> slog "BREAK1 dtmf_say"
>>>> slog "BREAK2 dtmf_say"
>>>> slog "BREAK3 dtmf_say"
>>>> slog "BREAK4 dtmf_say"
>>>> slog "BREAK5 dtmf_say"
>>>> slog "BREAK6 dtmf_say"
>>>>
>>>> clear %session.digits
>>>>        play 1
>>>>        play 2
>>>>        play 3
>>>>        play o k
>>>>        goto ::mytest
>>>>
>>>> program mytest
>>>>        slog "DTMF"
>>>>        sleep 15
>>>>        goto ::mytest
>>>>
>>>> ^dtmf
>>>>        string.1 %keyp
>>>>        collect %keyp count=1
>>>>        slog "DTMF %keyp detected"
>>>>        play &number %keyp
>>>>        play o k
>>>>        goto ::mytest
>>>>
>>>>
> ------------------------------------------------------------------------
>>> -----------
>>>> registration for 202 updated for 180 seconds
>>>> sip: event 0001; cid=0, did=0, rid=2, instance=4
>>>> registration for dtmf_say confirmed for 3600 seconds
>>>> sip: event 0001; cid=0, did=0, rid=1, instance=3
>>>> registration for 202 updated for 3600 seconds
>>>> sip: event 0001; cid=0, did=0, rid=1, instance=3
>>>> registration for 202 updated for 200 seconds
>>>> sip: event 0001; cid=0, did=0, rid=1, instance=3
>>>> registration for 202 confirmed for 200 seconds
>>>> sip: event 0005; cid=1, did=2, rid=0, instance=2
>>>> sip/0: state=idle, event=201, seq=1
>>>> sip/0: state=pickup, event=100, seq=1
>>>> sip/0: going active; associated with 202
>>>> sip: event 000f; cid=1, did=2, rid=0, instance=1
>>>> sip/0: state=pickup, event=503, seq=2
>>>> sip/0: state=run, event=100, seq=2
>>>> sip/0: BREAK1 dtmf_say
>>>> sip/0: BREAK2 dtmf_say
>>>> sip: event 0012; cid=1, did=2, rid=0, instance=5
>>>> sip: event 0012; cid=1, did=2, rid=0, instance=1
>>>> sip: event 0012; cid=1, did=2, rid=0, instance=5
>>>> sip: event 0019; cid=1, did=2, rid=0, instance=2
>>>> sip/0: closing call
>>>> sip/0: state=run, event=209, seq=5
>>>>
>>>>
>>>>
>>>> On 8/11/06, David Sugar <address@hidden> wrote:
>>>>> You received a SIP call accept, which has pickup wait for the sip
>>> driver
>>>>> "accept" timer to expire.  While waiting for the accept timer to
>>> expire,
>>>>> it received the additional sip events and then a close.
>>>>>
>>>>> Perhaps we should have an option to disable accept timer and go
>> right
>>> to
>>>>> the run state (by calling setRunning() instead).  This is currently
>>>>> controlled in modules/sip/session.cpp 1176;
>>>>>
>>>>>        else if(event->id == CALL_ACCEPTED)
>>>>>        {
>>>>>                startTimer(Driver::sip.accept_timer);
>>>>>                return true;
>>>>>        }
>>>>>
>>>>> in Session::enterPickup.
>>>>>
>>>>> Julien Chavanton wrote:
>>>>>> I am testing Bayonne2, the script I am calling address@hidden
>>> does
>>>>> not
>>>>>> start ?
>>>>>>
>>>>>>
>>>>>> address@hidden bayonne2-1.5.25]# vi
>>>>>> /usr/local/share/bayonne/scripts/dtmf_say.scr
>>>>>>
>>>>>>
> ------------------------------------------------------------------------
>>> ---------------------------------------
>>>>>> register.sip proxy=192.168.0.156
>>>>> uri=sip:address@hidden
>>>>>> secret=202 timeout=3600
>>>>>>
>>>>>> slog "BREAK1"
>>>>>> slog "BREAK2"
>>>>>> slog "BREAK3"
>>>>>>
> ------------------------------------------------------------------------
>>> ---------------------------------------
>>>>>>
>>>>>> registration for dtmf confirmed for 180 seconds
>>>>>> sip: event 0001; cid=0, did=0, rid=2, instance=4
>>>>>> registration for dtmf_say confirmed for 3600 seconds
>>>>>> sip: event 0005; cid=1, did=2, rid=0, instance=3
>>>>>> sip/0: state=idle, event=201, seq=1
>>>>>> sip/0: state=pickup, event=100, seq=1
>>>>>> sip/0: going active; associated with 202
>>>>>> sip: event 000f; cid=1, did=2, rid=0, instance=3
>>>>>> sip/0: state=pickup, event=503, seq=2
>>>>>> sip: event 0012; cid=1, did=2, rid=0, instance=3 (dmtf)
>>>>>> sip: event 0012; cid=1, did=2, rid=0, instance=4 (dmtf)
>>>>>> sip: event 0012; cid=1, did=2, rid=0, instance=2 (dmtf)
>>>>>> sip: event 0012; cid=1, did=2, rid=0, instance=5 (dmtf)
>>>>>> sip: event 0012; cid=1, did=2, rid=0, instance=5 (dmtf)
>>>>>> sip: event 0012; cid=1, did=2, rid=0, instance=4 (dmtf)
>>>>>> sip/0: closing call
>>>>>> sip/0: state=pickup, event=209, seq=11
>>>>>> sip/0: state=run, event=100, seq=11
>>>>>> sip/0: state=hangup, event=100, seq=11
>>>>>> _______________________________________________
>>>>>> Bayonne-devel mailing list
>>>>>> address@hidden
>>>>>> http://lists.gnu.org/mailman/listinfo/bayonne-devel
>>>> _______________________________________________
>>>> Bayonne-devel mailing list
>>>> address@hidden
>>>> http://lists.gnu.org/mailman/listinfo/bayonne-devel
>>> _______________________________________________
>>> Bayonne-devel mailing list
>>> address@hidden
>>> http://lists.gnu.org/mailman/listinfo/bayonne-devel
>>
>> _______________________________________________
>> Bayonne-devel mailing list
>> address@hidden
>> http://lists.gnu.org/mailman/listinfo/bayonne-devel
>>
>>
>> _______________________________________________
>> Bayonne-devel mailing list
>> address@hidden
>> http://lists.gnu.org/mailman/listinfo/bayonne-devel

reply via email to

[Prev in Thread] Current Thread [Next in Thread]