Ozone Specification

mpermar edited this page Sep 1, 2011 · 1 revision

Calls

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.

JID Format

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.

Incoming Calls

  <!-- Message comes from the Call's JID -->
  <presence to='16577@app.ozone.net/1' from='9f00061@call.ozone.net/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='9f00061@call.ozone.net/1' from='16577@app.ozone.net/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='9f00061@call.ozone.net/1' from='16577@app.ozone.net/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='9f00061@call.ozone.net/1' from='16577@app.ozone.net/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='9f00061@call.ozone.net/1' from='16577@app.ozone.net/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='9f00061@call.ozone.net/1' from='16577@app.ozone.net/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='9f00061@call.ozone.net/1' from='16577@app.ozone.net/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='9f00061@call.ozone.net/1' from='16577@app.ozone.net/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='9f00061@call.ozone.net/1' from='16577@app.ozone.net/1'>
    <unhold xmlns='urn:xmpp:ozone:1'/>
  </iq>

Outbound Calls

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='16577@app.ozone.net/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='16577@app.ozone.net/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='16577@app.ozone.net/1' from='9f00061@call.ozone.net/1'>
    <ringing xmlns='urn:xmpp:ozone:1' />
  </presence>
  
  <!-- The outgoing call has been answered (e.g. 200/OK) -->
  <presence to='16577@app.ozone.net/1' from='9f00061@call.ozone.net/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='16577@app.ozone.net/1' from='9f00061@call.ozone.net/1'>
    <end xmlns='urn:xmpp:ozone:1'>    
      <timeout />
    </end>
  </presence>
  
  <!-- Dial destination is busy and annot answer the call -->
  <presence to='16577@app.ozone.net/1' from='9f00061@call.ozone.net/1'>
    <end xmlns='urn:xmpp:ozone:1'>    
      <busy />
    </end>
  </presence>

  <!-- Dial destination rejected the call -->
  <presence to='16577@app.ozone.net/1' from='9f00061@call.ozone.net/1'>
    <end xmlns='urn:xmpp:ozone:1'>    
      <reject />
    </end>
  </presence>

  <!-- Ozone encountered a system error while dialing -->
  <presence to='16577@app.ozone.net/1' from='9f00061@call.ozone.net/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.

Handling caller hangup

If the caller hangs up the call Ozone will produce an <end/> event with a <hangup/> reason like so:

<presence to='16577@app.ozone.net/1' from='9f00061@call.ozone.net/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.

Forcing a call to end

Ozone clients can force a call to end by sending a <hangup/> command to the call's JID.

<iq id='1234' type='set' to='9f00061@call.ozone.net/1' from='16577@app.ozone.net/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.

Additional Call Events

<!-- Caller pressed the '#' key on their phone -->
<iq id='1234' type='set' to='9f00061@call.ozone.net/1' from='16577@app.ozone.net/1'>
  <dtmf xmlns='urn:xmpp:ozone:1' signal='#' />    
</iq>

Components

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='9f00061@call.ozone.net/1' from='16577@app.ozone.net/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='16577@app.ozone.net/1' to='9f00061@call.ozone.net/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.

Component Commands

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='9f00061@call.ozone.net/fgh4590' from='16577@app.ozone.net/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.

Component Events

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='9f00061@call.ozone.net/fgh4590' from='16577@app.ozone.net/1'>
   <complete xmlns='urn:xmpp:ozone:ext:1'>
     <success xmlns='urn:xmpp:ozone:say:complete:1' />
   </complete>
  </presence>

Say Component

  <iq id='1234' type='set' to='9f00061@call.ozone.net/1' from='16577@app.ozone.net/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>

Commands

  <!-- Client pause the say -->
  <iq id='1234' type='set' to='9f00061@call.ozone.net/fgh4590' from='16577@app.ozone.net/1'>
    <pause xmlns='urn:xmpp:ozone:say:1' />    
  </iq>

  <!-- Client resumes the say -->
  <iq id='1234' type='set' to='9f00061@call.ozone.net/fgh4590' from='16577@app.ozone.net/1'>
    <resume xmlns='urn:xmpp:ozone:say:1' />    
  </iq>

Events

  <!-- Playback completed successfully -->
  <presence to='16577@app.ozone.net/1' from='9f00061@call.ozone.net/fgh4590'>
    <complete xmlns='urn:xmpp:ozone:ext:1'>
      <success xmlns='urn:xmpp:ozone:say:complete:1' />
    </complete>
  </presence>
  
  <!-- Component was stopped -->
  <presence to='16577@app.ozone.net/1' from='9f00061@call.ozone.net/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='16577@app.ozone.net/1' from='9f00061@call.ozone.net/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='16577@app.ozone.net/1' from='9f00061@call.ozone.net/fgh4590'>
    <complete xmlns='urn:xmpp:ozone:ext:1'>
      <error xmlns='urn:xmpp:ozone:ext:complete:1'>
        Something really bad happened
      </error>
    </complete>
  </presence>
  

Ask Component

  <iq id='1234' type='set' to='9f00061@call.ozone.net/1' from='16577@app.ozone.net/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

Events

  <!-- Successfull Input -->
  <presence to='16577@app.ozone.net/1' from='9f00061@call.ozone.net/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='16577@app.ozone.net/1' from='9f00061@call.ozone.net/fgh4590'>
    <complete xmlns='urn:xmpp:ozone:ext:1'>
      <nomatch xmlns='urn:xmpp:ozone:ask:complete:1' />
    </complete>
  </presence>  

  <!-- No Input Provided -->
  <presence to='16577@app.ozone.net/1' from='9f00061@call.ozone.net/fgh4590'>
    <complete xmlns='urn:xmpp:ozone:ext:1'>
      <noinput xmlns='urn:xmpp:ozone:ask:complete:1' />
    </complete>
  </presence>  

  <!-- Component was stopped -->
  <presence to='16577@app.ozone.net/1' from='9f00061@call.ozone.net/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='16577@app.ozone.net/1' from='9f00061@call.ozone.net/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='16577@app.ozone.net/1' from='9f00061@call.ozone.net/fgh4590'>
    <complete xmlns='urn:xmpp:ozone:ext:1'>
      <error xmlns='urn:xmpp:ozone:ext:complete:1'>
        Something really bad happened
      </error>
    </complete>
  </presence>

Transfer Component

  <iq id='1234' type='set' to='9f00061@call.ozone.net/1' from='16577@app.ozone.net/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>

Events

  <!-- 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='16577@app.ozone.net/1' from='9f00061@call.ozone.net/fgh4590'>
    <complete xmlns='urn:xmpp:ozone:ext:1'>
      <success xmlns='urn:xmpp:ozone:transfer:complete:1' />
    </complete>
  </presence>

  <!-- Timeout Expired  -->
  <presence to='16577@app.ozone.net/1' from='9f00061@call.ozone.net/fgh4590'>
    <complete xmlns='urn:xmpp:ozone:ext:1'>
      <timeout xmlns='urn:xmpp:ozone:transfer:complete:1' />
    </complete>
  </presence>

  <!-- Caller pressed terminator  -->
  <presence to='16577@app.ozone.net/1' from='9f00061@call.ozone.net/fgh4590'>
    <complete xmlns='urn:xmpp:ozone:ext:1'>
      <terminator xmlns='urn:xmpp:ozone:transfer:complete:1' />
    </complete>
  </presence>

  <!-- Destination was busy  -->
  <presence to='16577@app.ozone.net/1' from='9f00061@call.ozone.net/fgh4590'>
    <complete xmlns='urn:xmpp:ozone:ext:1'>
      <busy xmlns='urn:xmpp:ozone:transfer:complete:1' />
    </complete>
  </presence>

  <!-- Destination rejected the call  -->
  <presence to='16577@app.ozone.net/1' from='9f00061@call.ozone.net/fgh4590'>
    <complete xmlns='urn:xmpp:ozone:ext:1'>
      <reject xmlns='urn:xmpp:ozone:transfer:complete:1' />
    </complete>
  </presence>

  <!-- Component was stopped -->
  <presence to='16577@app.ozone.net/1' from='9f00061@call.ozone.net/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='16577@app.ozone.net/1' from='9f00061@call.ozone.net/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='16577@app.ozone.net/1' from='9f00061@call.ozone.net/fgh4590'>
    <complete xmlns='urn:xmpp:ozone:ext:1'>
      <error xmlns='urn:xmpp:ozone:ext:complete:1'>
        Something really bad happened
      </error>
    </complete>
  </presence>

Conference Component

  <iq id='1234' type='set' to='9f00061@call.ozone.net/1' from='16577@app.ozone.net/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>

Commands

  <!-- Mute this participant -->
  <iq id='1234' type='set' to='9f00061@ozone.net/d951cc41' from='16577@app.ozone.net/1'>
    <mute xmlns='urn:xmpp:ozone:conference:1' />
  </iq>

  <!-- Unmute this participant -->
  <iq id='1234' type='set' to='9f00061@ozone.net/d951cc41' from='16577@app.ozone.net/1'>
    <unmute xmlns='urn:xmpp:ozone:conference:1' />
  </iq>

  <!-- Kick this participant, Do we need this? -->
  <iq id='1234' type='set' to='9f00061@ozone.net/d951cc41' from='16577@app.ozone.net/1'>
    <kick xmlns='urn:xmpp:ozone:conference:1'>asshole</kick>
  </iq>

Events

  <!-- Indicates that this participant has been put on hold -->
  <presence to='16577@app.ozone.net/1' from='9f00061@ozone.net/d951cc41'>
    <on-hold xmlns='urn:xmpp:ozone:conference:1'/>
  </presence>

  <!-- Indicates that this participant has been put back into the conference -->
  <presence to='16577@app.ozone.net/1' from='9f00061@ozone.net/d951cc41'>
    <off-hold xmlns='urn:xmpp:ozone:conference:1'/>
  </presence>

  <!-- Participant was kicked from the conference -->
  <presence to='16577@app.ozone.net/1' from='9f00061@ozone.net/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='16577@app.ozone.net/1' from='9f00061@ozone.net/d951cc41'>
    <complete xmlns='urn:xmpp:ozone:ext:1'>
      <terminator xmlns='urn:xmpp:ozone:conference:complete:1' />
    </complete>
  </presence>
  
  <!-- Component was stopped -->
  <presence to='16577@app.ozone.net/1' from='9f00061@call.ozone.net/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='16577@app.ozone.net/1' from='9f00061@call.ozone.net/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='16577@app.ozone.net/1' from='9f00061@call.ozone.net/fgh4590'>
    <complete xmlns='urn:xmpp:ozone:ext:1'>
      <error xmlns='urn:xmpp:ozone:ext:complete:1'>
        Something really bad happened
      </error>
    </complete>
  </presence>