-
Notifications
You must be signed in to change notification settings - Fork 4
Ozone Specification
The Ozone protocol primarily deals with calls. Inbound calls originate from the PSTN or via SIP and are offered to Ozone clients via XMPP using a Jabber Identifier (JID). Each call is in turn represented by it's own unique JID allowing a two way conversation between the Ozone client and the server that's handling the call signaling and media.
The JID follows a specific format. In XMPP the JID is constructed as
<node>@<domain>/<resource>
For Ozone, the <node>
portion of the JID always represents the call ID. The <resource>
, when present, represents the affected command ID.
<!-- Message comes from the Call's JID -->
<presence to='[email protected]/1' from='[email protected]/1'>
<offer xmlns='urn:xmpp:ozone:1'
to='tel:+18003211212'
from='tel:+13058881212'>
<!-- Signaling (e.g. SIP) Headers -->
<header name='Via' value='192.168.0.1' />
<header name='Contact' value='192.168.0.1' />
</offer>
</presence>
The Ozone client can now control the call by using one of the following commands.
<!-- Accept (e.g. SIP 180/Ringing). Only applies to incoming calls. -->
<iq id='1234' type='set' to='[email protected]/1' from='[email protected]/1'>
<accept xmlns='urn:xmpp:ozone:1'>
<!-- Sample Headers (optional) -->
<header name="x-skill" value="agent" />
<header name="x-customer-id" value="8877" />
</accept>
</iq>
<!-- Answer (e.g. SIP 200/OK). Only applies to incoming calls. -->
<iq id='1234' type='set' to='[email protected]/1' from='[email protected]/1'>
<answer xmlns='urn:xmpp:ozone:1'>
<!-- Sample Headers (optional) -->
<header name="x-skill" value="agent" />
<header name="x-customer-id" value="8877" />
</answer>
</iq>
<!-- Redirect (e.g. SIP 302/Redirect). Only applies to incoming calls. -->
<iq id='1234' type='set' to='[email protected]/1' from='[email protected]/1'>
<redirect to='tel:+14152226789' xmlns='urn:xmpp:ozone:1'>
<!-- Sample Headers (optional) -->
<header name="x-skill" value="agent" />
<header name="x-customer-id" value="8877" />
</redirect>
</iq>
A call can also be rejected. Rejections can include an optional rejection reason. Rejection reasons are one of
<busy/>
, <decline/>
or <error/>
. If not specified, <decline/>
is used as the default reason.
<!-- Decline (.g. SIP 603/Decline). Only applies to incoming calls. -->
<iq id='1234' type='set' to='[email protected]/1' from='[email protected]/1'>
<reject xmlns='urn:xmpp:ozone:1'>
<decline />
<!-- Sample Headers (optional) -->
<header name="x-reason-internal" value="bad-skill" />
</reject>
</iq>
<!-- Busy (.g. SIP 486/Busy). Only applies to incoming calls. -->
<iq id='1234' type='set' to='[email protected]/1' from='[email protected]/1'>
<reject xmlns='urn:xmpp:ozone:1'>
<busy />
<!-- Sample Headers (optional) -->
<header name="x-busy-detail" value="out of licenses" />
</reject>
</iq>
<!-- Error (.g. SIP 500/Internal Server Error). Only applies to incoming calls. -->
<iq id='1234' type='set' to='[email protected]/1' from='[email protected]/1'>
<reject xmlns='urn:xmpp:ozone:1'>
<error />
<!-- Sample Headers (optional) -->
<header name="x-error-detail" value="soem descriptive error message" />
</reject>
</iq>
A call can be set to hold status with the hold command:
<iq id='1234' type='set' to='[email protected]/1' from='[email protected]/1'>
<hold xmlns='urn:xmpp:ozone:1'/>
</iq>
A call be set to unhold status with the unhold command:
<iq id='1234' type='set' to='[email protected]/1' from='[email protected]/1'>
<unhold xmlns='urn:xmpp:ozone:1'/>
</iq>
Ozone clients can initiate outbound calls using the <dial />
command.
<!-- Handled by the domain controller which picks a random Ozone Server -->
<iq id='123456' type='set' to='call.ozone.net' from='[email protected]/1'>
<dial to='tel:+13055195825' from='tel:+14152226789' xmlns='urn:xmpp:ozone:1'>
<header name="x-skill" value="agent" />
<header name="x-customer-id" value="8877" />
</dial>
</iq>
<iq id='123456' type='result' to='[email protected]/1' from='call.ozone.net'>
<!-- The Call's ID -->
<ref id='9f00061' />
</iq>
The client will then begin to receive progress events as the call makes it's way through the network.
<!-- Far end has accepted the call and is ringing (e.g. 180/Ringing) -->
<presence to='[email protected]/1' from='[email protected]/1'>
<ringing xmlns='urn:xmpp:ozone:1' />
</presence>
<!-- The outgoing call has been answered (e.g. 200/OK) -->
<presence to='[email protected]/1' from='[email protected]/1'>
<answered xmlns='urn:xmpp:ozone:1' />
</presence>
If for some reason the call is not accepted by the far end, the Ozone client will receive an <end/>
event indicating the reason for the failure.
<!-- Dial destination did not answer within the timeout period -->
<presence to='[email protected]/1' from='[email protected]/1'>
<end xmlns='urn:xmpp:ozone:1'>
<timeout />
</end>
</presence>
<!-- Dial destination is busy and annot answer the call -->
<presence to='[email protected]/1' from='[email protected]/1'>
<end xmlns='urn:xmpp:ozone:1'>
<busy />
</end>
</presence>
<!-- Dial destination rejected the call -->
<presence to='[email protected]/1' from='[email protected]/1'>
<end xmlns='urn:xmpp:ozone:1'>
<reject />
</end>
</presence>
<!-- Ozone encountered a system error while dialing -->
<presence to='[email protected]/1' from='[email protected]/1'>
<end xmlns='urn:xmpp:ozone:1'>
<error>Lucy, you got some 'splainin to do</error>
</end>
</presence>
Note: A Ozone <end/>
indicates that the call has been disconnected and that no more events are possible for this call. Therefore, the <end/>
event is a perfect point for clients to clean up resources related to the controlling of the call.
If the caller hangs up the call Ozone will produce an <end/>
event with a <hangup/>
reason like so:
<presence to='[email protected]/1' from='[email protected]/1'>
<end xmlns='urn:xmpp:ozone:1'>
<hangup/>
</end>
</presence>
Note: A Ozone <end/>
indicates that the call has been disconnected and that no more events are possible for this call. Therefore, the <end/>
event is a perfect point for clients to clean up resources related to the controlling of the call.
Ozone clients can force a call to end by sending a <hangup/>
command to the call's JID.
<iq id='1234' type='set' to='[email protected]/1' from='[email protected]/1'>
<hangup xmlns='urn:xmpp:ozone:1'>
<!-- Sample Headers (optional) -->
<header name="x-reason-internal" value="bad-skill" />
</hangup>
</iq>
NOTE: The client will still receive an <end/>
event indicating that that call has been disconnected and that no further events or commands are possible.
<!-- Caller pressed the '#' key on their phone -->
<iq id='1234' type='set' to='[email protected]/1' from='[email protected]/1'>
<dtmf xmlns='urn:xmpp:ozone:1' signal='#' />
</iq>
Components extend the Ozone protocol by providing additional media and call control functionality.
Components are started by sending a specialized command to the Ozone server. This example shows the use of the <say xmlns='urn:xmpp:ozone:say:1'/>
component. Don't worry about the specifics of the <say/>
element for now. We'll discuss each component in detail in the folowing chapters. The key point here is that a component request is being sent to the call's JID.
NOTE: You can easily spot a component request because the namespace will be in the format urn:xmpp:ozone:COMPONENT_NAME:1
<iq id='1234' type='set' to='[email protected]/1' from='[email protected]/1'>
<say xmlns='urn:xmpp:ozone:say:1'
voice='allison'>
<audio src='http://acme.com/greeting.mp3'>
Thanks for calling ACME company
</audio>
<audio src='http://acme.com/package-shipped.mp3'>
Your package was shipped on
</audio>
<say-as interpret-as='date'>12/01/2011</say-as>
</say>
</iq>
The Ozone server will validate the component request and attach a new instance of the component to the call. In a happy day scenario the client will immediately receive an IQ result containing the newly created component's ID. The component's ID is combined with the call's JID to control the component (e.g. pause, resume, stop, etc.) and to corelate events coming from the component as well.
A component's JID is calculated by combining the call's JID with the newly created component's ID like so: <call-id>@<ozone-domain>/<component-id>
<!-- Server responds a unique ID -->
<iq id='1234' type='result' to='[email protected]/1' to='[email protected]/1'>
<ref id='fgh4590' xmlns='urn:xmpp:ozone:1' />
</iq>
NOTE: Remember that Ozone executes components asynchronously and in many cases more than one component can run at the same time. For example, you can have the <record xmlns='' />
component running throught the entire while you interact with the user using the "say" and "ask" components resulting in the entire call being recorded.
Components are controlled by sending command messages to their unique JID. The only command required by all components is the <stop/>
command.
<iq id='1234' type='set' to='[email protected]/fgh4590' from='[email protected]/1'>
<stop xmlns='urn:xmpp:ozone:1' />
</iq>
As you'll see in the following chapters, component developers can get very creative with the command they support allowing for some really interesting capabilities. For example, the ability to pause and resum audio playback as well as muting and unmuting the caller's microphone while in a conference.
Events are specialized lifecycle messages that flow from a component instance to the Ozone client that's controlling the call. As you'll see in the following chapters, component events are very powerful and can provide great insight into a running application.
The only event required by all components is the <complete xmlns='urn:xmpp:ozone:ext:complete:1' />
. This is an example complete event produced by the <say urn:xmpp:ozone:say:1/>
component when audio playback has completed succesfully.
<presence to='[email protected]/fgh4590' from='[email protected]/1'>
<complete xmlns='urn:xmpp:ozone:ext:1'>
<success xmlns='urn:xmpp:ozone:say:complete:1' />
</complete>
</presence>
<iq id='1234' type='set' to='[email protected]/1' from='[email protected]/1'>
<say xmlns='urn:xmpp:ozone:say:1'
voice='allison'>
<audio src='http://acme.com/greeting.mp3'>
Thanks for calling ACME company
</audio>
<audio src='http://acme.com/package-shipped.mp3'>
Your package was shipped on
</audio>
<say-as interpret-as='date'>12/01/2011</say-as>
</say>
</iq>
<!-- Client pause the say -->
<iq id='1234' type='set' to='[email protected]/fgh4590' from='[email protected]/1'>
<pause xmlns='urn:xmpp:ozone:say:1' />
</iq>
<!-- Client resumes the say -->
<iq id='1234' type='set' to='[email protected]/fgh4590' from='[email protected]/1'>
<resume xmlns='urn:xmpp:ozone:say:1' />
</iq>
<!-- Playback completed successfully -->
<presence to='[email protected]/1' from='[email protected]/fgh4590'>
<complete xmlns='urn:xmpp:ozone:ext:1'>
<success xmlns='urn:xmpp:ozone:say:complete:1' />
</complete>
</presence>
<!-- Component was stopped -->
<presence to='[email protected]/1' from='[email protected]/fgh4590'>
<complete xmlns='urn:xmpp:ozone:ext:1'>
<stop xmlns='urn:xmpp:ozone:ext:complete:1' />
</complete>
</presence>
<!-- Component completed because the call was disconnected -->
<presence to='[email protected]/1' from='[email protected]/fgh4590'>
<complete xmlns='urn:xmpp:ozone:ext:1'>
<hangup xmlns='urn:xmpp:ozone:ext:complete:1' />
</complete>
</presence>
<!-- Component completed because the call was disconnected -->
<presence to='[email protected]/1' from='[email protected]/fgh4590'>
<complete xmlns='urn:xmpp:ozone:ext:1'>
<error xmlns='urn:xmpp:ozone:ext:complete:1'>
Something really bad happened
</error>
</complete>
</presence>
<iq id='1234' type='set' to='[email protected]/1' from='[email protected]/1'>
<ask xmlns='urn:xmpp:ozone:ask:1'
bargein='true'
min-confidence='0.3'
mode='speech|dtmf|any'
recognizer='en-US'
terminator='#'
timeout='12000'>
<prompt voice='allison'>
Please enter your four digit pin
</prompt>
<choices content-type='application/grammar+voxeo'>
[4 DIGITS]
</choices>
</ask>
</iq>
<choices /> is required
<!-- Successfull Input -->
<presence to='[email protected]/1' from='[email protected]/fgh4590'>
<complete xmlns='urn:xmpp:ozone:ext:1'>
<success mode="speech" confidence="0.45" xmlns='urn:xmpp:ozone:ask:complete:1'>
<interpretation>1234</interpretation>
<utterance>one two three four</utterance>
</success>
</complete>
</presence>
<!-- Incorrect Input -->
<presence to='[email protected]/1' from='[email protected]/fgh4590'>
<complete xmlns='urn:xmpp:ozone:ext:1'>
<nomatch xmlns='urn:xmpp:ozone:ask:complete:1' />
</complete>
</presence>
<!-- No Input Provided -->
<presence to='[email protected]/1' from='[email protected]/fgh4590'>
<complete xmlns='urn:xmpp:ozone:ext:1'>
<noinput xmlns='urn:xmpp:ozone:ask:complete:1' />
</complete>
</presence>
<!-- Component was stopped -->
<presence to='[email protected]/1' from='[email protected]/fgh4590'>
<complete xmlns='urn:xmpp:ozone:ext:1'>
<stop xmlns='urn:xmpp:ozone:ext:complete:1' />
</complete>
</presence>
<!-- Component completed because the call was disconnected -->
<presence to='[email protected]/1' from='[email protected]/fgh4590'>
<complete xmlns='urn:xmpp:ozone:ext:1'>
<hangup xmlns='urn:xmpp:ozone:ext:complete:1' />
</complete>
</presence>
<!-- Component completed because the call was disconnected -->
<presence to='[email protected]/1' from='[email protected]/fgh4590'>
<complete xmlns='urn:xmpp:ozone:ext:1'>
<error xmlns='urn:xmpp:ozone:ext:complete:1'>
Something really bad happened
</error>
</complete>
</presence>
<iq id='1234' type='set' to='[email protected]/1' from='[email protected]/1'>
<transfer xmlns='urn:xmpp:ozone:transfer:1'
from='tel:+14152226789'
terminator='*'
timeout='120000'
answer-on-media='true'>
<to>tel:+4159996565</to>
<to>tel:+3059871234</to>
<ring voice='allison'>
<audio src='http://acme.com/transfering.mp3'>
Please wait while your call is being transfered.
</audio>
</ring>
<header name="x-skill" value="agent" />
<header name="x-customer-id" value="8877" />
</transfer>
</iq>
<!-- Transfer completed and B leg disconnected. The A leg is joined back to
the media server and is free to run additional components -->
<presence to='[email protected]/1' from='[email protected]/fgh4590'>
<complete xmlns='urn:xmpp:ozone:ext:1'>
<success xmlns='urn:xmpp:ozone:transfer:complete:1' />
</complete>
</presence>
<!-- Timeout Expired -->
<presence to='[email protected]/1' from='[email protected]/fgh4590'>
<complete xmlns='urn:xmpp:ozone:ext:1'>
<timeout xmlns='urn:xmpp:ozone:transfer:complete:1' />
</complete>
</presence>
<!-- Caller pressed terminator -->
<presence to='[email protected]/1' from='[email protected]/fgh4590'>
<complete xmlns='urn:xmpp:ozone:ext:1'>
<terminator xmlns='urn:xmpp:ozone:transfer:complete:1' />
</complete>
</presence>
<!-- Destination was busy -->
<presence to='[email protected]/1' from='[email protected]/fgh4590'>
<complete xmlns='urn:xmpp:ozone:ext:1'>
<busy xmlns='urn:xmpp:ozone:transfer:complete:1' />
</complete>
</presence>
<!-- Destination rejected the call -->
<presence to='[email protected]/1' from='[email protected]/fgh4590'>
<complete xmlns='urn:xmpp:ozone:ext:1'>
<reject xmlns='urn:xmpp:ozone:transfer:complete:1' />
</complete>
</presence>
<!-- Component was stopped -->
<presence to='[email protected]/1' from='[email protected]/fgh4590'>
<complete xmlns='urn:xmpp:ozone:ext:1'>
<stop xmlns='urn:xmpp:ozone:ext:complete:1' />
</complete>
</presence>
<!-- Component completed because the call was disconnected -->
<presence to='[email protected]/1' from='[email protected]/fgh4590'>
<complete xmlns='urn:xmpp:ozone:ext:1'>
<hangup xmlns='urn:xmpp:ozone:ext:complete:1' />
</complete>
</presence>
<!-- Component completed because the call was disconnected -->
<presence to='[email protected]/1' from='[email protected]/fgh4590'>
<complete xmlns='urn:xmpp:ozone:ext:1'>
<error xmlns='urn:xmpp:ozone:ext:complete:1'>
Something really bad happened
</error>
</complete>
</presence>
<iq id='1234' type='set' to='[email protected]/1' from='[email protected]/1'>
<conference xmlns='urn:xmpp:ozone:conference:1'
name='1234'
mute='false'
terminator='*'
tone-passthrough='true'
moderator='true'>
<announcement voice="allison">
Jose de Castro has entered the conference
</announcement>
<music voice="herbert">
The moderator how not yet joined.. Listen to this awesome music while you wait.
<audio src='http://www.yanni.com/music/awesome.mp3' />
</music>
</conference>
</iq>
<!-- Mute this participant -->
<iq id='1234' type='set' to='[email protected]/d951cc41' from='[email protected]/1'>
<mute xmlns='urn:xmpp:ozone:conference:1' />
</iq>
<!-- Unmute this participant -->
<iq id='1234' type='set' to='[email protected]/d951cc41' from='[email protected]/1'>
<unmute xmlns='urn:xmpp:ozone:conference:1' />
</iq>
<!-- Kick this participant, Do we need this? -->
<iq id='1234' type='set' to='[email protected]/d951cc41' from='[email protected]/1'>
<kick xmlns='urn:xmpp:ozone:conference:1'>asshole</kick>
</iq>
<!-- Indicates that this participant has been put on hold -->
<presence to='[email protected]/1' from='[email protected]/d951cc41'>
<on-hold xmlns='urn:xmpp:ozone:conference:1'/>
</presence>
<!-- Indicates that this participant has been put back into the conference -->
<presence to='[email protected]/1' from='[email protected]/d951cc41'>
<off-hold xmlns='urn:xmpp:ozone:conference:1'/>
</presence>
<!-- Participant was kicked from the conference -->
<presence to='[email protected]/1' from='[email protected]/d951cc41'>
<complete xmlns='urn:xmpp:ozone:ext:1'>
<kick xmlns='urn:xmpp:ozone:conference:complete:1'>wouldn't stop talking</kick>
</complete>
</presence>
<!-- Participant pressed the terminator -->
<presence to='[email protected]/1' from='[email protected]/d951cc41'>
<complete xmlns='urn:xmpp:ozone:ext:1'>
<terminator xmlns='urn:xmpp:ozone:conference:complete:1' />
</complete>
</presence>
<!-- Component was stopped -->
<presence to='[email protected]/1' from='[email protected]/fgh4590'>
<complete xmlns='urn:xmpp:ozone:ext:1'>
<stop xmlns='urn:xmpp:ozone:ext:complete:1' />
</complete>
</presence>
<!-- Component completed because the call was disconnected -->
<presence to='[email protected]/1' from='[email protected]/fgh4590'>
<complete xmlns='urn:xmpp:ozone:ext:1'>
<hangup xmlns='urn:xmpp:ozone:ext:complete:1' />
</complete>
</presence>
<!-- Component completed because the call was disconnected -->
<presence to='[email protected]/1' from='[email protected]/fgh4590'>
<complete xmlns='urn:xmpp:ozone:ext:1'>
<error xmlns='urn:xmpp:ozone:ext:complete:1'>
Something really bad happened
</error>
</complete>
</presence>