Setting up a conference bridge with FreeSWITCH

Here is a short description how I set up my conference bridge with FreeSWITCH (All PIN numbers and extensions are made up). The requirements were as follows:

  1. Multiple conference rooms for different projects. Each room has its own moderator credentials.
  2. The same access phone numbers for all rooms.
  3. Regular users have to enter 4 digits to get into a conference (Moderator can dial more digits, as he or she is usually more prepared).
  4. International access at local call rates.
  5. Automatic call recording. Recordings are stored in separate folders for each room, so that simple Apache authentication can be used for selected rooms.

IVR menu

This IVR menu is a simple way to request 4 digits for the room number. It does not validate if such room exists — in case of invalid entry, the call is disconnected. This is a simple way of “authentication”: if the digits are random enough, the service is protected well enough from misuse.

File: conf/ivr_menus/select_conference.xml
<include>
  <menu name="select_conference"
      greet-long="$${sounds_dir}/dvop/enter_conference_room_number.wav"
      greet-short="$${sounds_dir}/dvop/enter_conference_room_number.wav"
      invalid-sound="ivr/ivr-that_was_an_invalid_entry.wav"
      exit-sound="voicemail/vm-goodbye.wav"
      confirm-macro=""
      confirm-key=""
      tts-engine="flite"
      tts-voice="rms"
      confirm-attempts="3"
      timeout="10000"
      inter-digit-timeout="2000"
      max-failures="3"
      max-timeouts="3"
      digit-len="4">
    <entry digits="/^(\d\d\d\d)/" action="menu-exec-app"
           param="transfer conf_$1 XML default"/>
  </menu>
</include>

Dial plan

Conferences are part of the default context. The extension 7500 is used to connect to the common IVR menu, and the IVR transfers the call into a “conf_XXXX” extension, which corresponds to each conference room. Alternatively, a special context could be created for conferences, and each numeric extension would be a room number in that context. The trrtrr.net is a public project for technology workshop, and it has its own profile, and also the users are muted by default. This example is limited to two conference rooms, and more rooms can be easily added. It is also possible to deliver the room configuration via mod_xml_curl.

File: conf/dialplan/default/70_conferences.xml
<include>
  <!-- conferencing -->  
  <extension name="conference_7500">
    <condition field="destination_number" expression="^7500$">
      <action application="answer"/>
      <action application="sleep" data="500"/>
      <action application="ivr" data="select_conference"/>
    </condition>
  </extension>

  <!-- K-Open.com conference. Room number: 8465, Moderator: 5384, PIN: 9264 -->

  <extension name="conf-k_open">
    <condition field="destination_number" expression="^conf_8465$">
      <action application="answer"/>
      <action application="sleep" data="500"/>
      <action application="playback" data="$${sounds_dir}/dvop/this_call_is_automatically_recorded.wav"/>
      <action application="conference" data="k_open@default"/>
    </condition>
  </extension>

  <extension name="conf-k_open_mod">
    <condition field="destination_number" expression="^conf_5384$">
      <action application="answer"/>
      <action application="sleep" data="500"/>
      <action application="playback" data="$${sounds_dir}/dvop/this_call_is_automatically_recorded.wav"/>
      <action application="conference" data="k_open@default+9264+flags{moderator} "/>
    </condition>
  </extension>
  <!-- TrrTrr.net conference. Room number: 2648, Moderator: 9472, PIN: 7536 -->

  <extension name="conf-trrtrr">
    <condition field="destination_number" expression="^conf_2648$">
      <action application="answer"/>
      <action application="sleep" data="500"/>
      <action application="playback" data="$${sounds_dir}/dvop/this_call_is_automatically_recorded.wav"/>
      <action application="playback" data="$${sounds_dir}/dvop/you_are_muted_press_0_to_unmute_or_mute.wav"/>
      <action application="conference" data="trrtrr@trrtrr++flags{mute}"/>
    </condition>
  </extension>

  <extension name="conf-trrtrr_mod">
    <condition field="destination_number" expression="^conf_9472$">
      <action application="answer"/>
      <action application="sleep" data="500"/>
      <action application="playback" data="$${sounds_dir}/dvop/this_call_is_automatically_recorded.wav"/>
      <action application="conference" data="trrtrr@trrtrr+7536+flags{moderator} "/>
    </condition>
  </extension>
</include>

Conference profiles

The only difference between the two profiles is that trrtrr.net does not play any sound when someone enters the conference. Both profiles require the moderator to come in. Also  both profiles record the call automatically in a WAV file in a directory dedicated to the conference room.

File: conf/autoload_configs/conference.conf.xml
 <profiles>
    <profile name="default">
      <param name="cdr-log-dir" value="auto"/>
      <param name="domain" value="$${domain}"/>
      <param name="rate" value="8000"/>
      <param name="interval" value="20"/>
      <param name="energy-level" value="0"/>
      <param name="member-flags" value="waste"/>
      <param name="conference-flags" value="wait-mod"/>

      <param name="ack-sound" value="beep.wav"/>
      <param name="nack-sound" value="beeperr.wav"/>
      <param name="muted-sound" value="conference/conf-muted.wav"/>
      <param name="unmuted-sound" value="conference/conf-unmuted.wav"/>
      <param name="alone-sound" value="conference/conf-alone.wav"/>
      <param name="moh-sound" value="$${hold_music}"/>
      <param name="enter-sound" value="tone_stream://&gt;=1;+=.2;%(500,0,500,800)"/>
      <param name="exit-sound" value="tone_stream://&gt;=2;+=.2;%(800,0,75,220)"/>      
      <param name="kicked-sound" value="conference/conf-kicked.wav"/>
      <param name="locked-sound" value="conference/conf-locked.wav"/>
      <param name="is-locked-sound" value="conference/conf-is-locked.wav"/>
      <param name="is-unlocked-sound" value="conference/conf-is-unlocked.wav"/>
      <param name="pin-sound" value="conference/conf-pin.wav"/>
      <param name="bad-pin-sound" value="conference/conf-bad-pin.wav"/>

      <!-- <param name="pin" value=""/> -->
      <!-- <param name="moderator-pin" value="3434"/> -->
      <param name="pin-retries" value="3"/>

      <param name="caller-id-name" value="$${outbound_caller_name}"/>
      <param name="caller-id-number" value="$${outbound_caller_id}"/>

      <param name="comfort-noise" value="true"/>

      <param name="ivr-dtmf-timeout" value="500"/>
      <param name="ivr-input-timeout" value="0"/>

      <param name="auto-record" value="/opt/freeswitch/recordings/${conference_name}/${conference_name}_${strftime(%Y-%m-%d-%H-%M-%S)}.wav"/>
   </profile>      

   <profile name="trrtrr">
      <param name="cdr-log-dir" value="auto"/>
      <param name="domain" value="$${domain}"/>
      <param name="rate" value="8000"/>
      <param name="interval" value="20"/>
      <param name="energy-level" value="0"/>
      <param name="member-flags" value="waste"/>
      <param name="conference-flags" value="wait-mod"/>

      <param name="ack-sound" value="beep.wav"/>
      <param name="nack-sound" value="beeperr.wav"/>
      <param name="muted-sound" value="conference/conf-muted.wav"/>
      <param name="unmuted-sound" value="conference/conf-unmuted.wav"/>
      <param name="alone-sound" value="conference/conf-alone.wav"/>
      <param name="moh-sound" value="$${hold_music}"/>
      <!-- <param name="enter-sound" value="tone_stream://%(200,0,500,600,700)"/> -->
      <!-- <param name="exit-sound" value="tone_stream://%(500,0,300,200,100,50,25)"/> -->
      <param name="kicked-sound" value="conference/conf-kicked.wav"/>
      <param name="locked-sound" value="conference/conf-locked.wav"/>
      <param name="is-locked-sound" value="conference/conf-is-locked.wav"/>
      <param name="is-unlocked-sound" value="conference/conf-is-unlocked.wav"/>
      <param name="pin-sound" value="conference/conf-pin.wav"/>
      <param name="bad-pin-sound" value="conference/conf-bad-pin.wav"/>

      <!-- <param name="pin" value=""/> -->
      <!-- <param name="moderator-pin" value="3434"/> -->
      <param name="pin-retries" value="3"/>

      <param name="caller-id-name" value="$${outbound_caller_name}"/>
      <param name="caller-id-number" value="$${outbound_caller_id}"/>

      <param name="comfort-noise" value="true"/>

      <param name="ivr-dtmf-timeout" value="500"/>
      <param name="ivr-input-timeout" value="0"/>

      <param name="auto-record" value="/opt/freeswitch/recordings/${conference_name}/${conference_name}_${strftime(%Y-%m-%d-%H-%M-%S)}.wav"/>
   </profile>

Connecting the external world

There are several ways to reach my conference bridge.

Access from a PSTN trunk

I have a SIP trunk with 10 Swiss numbers, so one is dedicated for the conference bridge. Also another number connects to an automatic attendant IVR which has also the conference bridge as one of the choices.

File: conf/dialplan/public/00_inbound_did.xml
  <!-- Conference -->
  <extension name="public_did_05">
    <condition field="destination_number" expression="^$${default_did_prefix}5$">
      <action application="set" data="domain_name=$${domain}"/>
      <action application="transfer" data="7500 XML default"/>
    </condition>
  </extension>

Access via SIP URI

Anyone with a SIP client supporting SIP URI is able to access the conference bridge. Not that anyone uses that, but I can forward calls from some international access providers to my bridge.

File: conf/dialplan/public/65_conference.xml
  <extension name="pub_conference">
    <condition field="destination_number" expression="^conference$">
      <action application="transfer" data="7500 XML default"/>
    </condition>
  </extension>
  <extension name="pub_conf_trrtrr">
    <condition field="destination_number" expression="^conf.trrtrr$">
      <action application="transfer" data="conf_2648 XML default"/>
    </condition>
  </extension>

Access via INUM

INUM is an initiative of Voxbone which provides anyone with a country-independent number. Also some telephony providers route calls to INUM numbers directly. In other cases, anyone can call a local access number in their country and dial the last 9 digits or the whole INUM number. Many VoIP providers offer free INUM numbers, but some of them limit the number of simultaneous calls to one. V-Global allows unlimited number of concurrent INUM calls, but they limit one account to one INUM number only.

File: conf/sip_profiles/external/v-global.net.xml
<include>
  <gateway name="v-global-conf">
    <param name="username" value="XXXXXXX"/>
    <param name="password" value="XXXXXXX"/>
    <param name="extension" value="conference"/>
    <param name="expire-seconds" value="125"/>
    <param name="register" value="true"/>
    <param name="register-transport" value="udp"/>
    <param name="proxy" value="sip1.vgsip.com"/>
    <param name="retry-seconds" value="30"/>
    <param name="caller-id-in-from" value="false"/>
    <param name="ping" value="27"/>
  </gateway>
</include>

Access from Skype

This topic is already covered in detail in other postings in this blog.

Advertisements

, , ,

  1. #1 by Peter on July 9, 2013 - 12:15 pm

    That was really helpful..

    I have a dial plan in which when you call to one conference room it ll connect a group of users to a particular conference. I have different conference rooms which does the same. Now I would like to make a conference with the existing conferences as its members.

    I want to know, if it is possible to set up a conference call for these conferences ??

    • #2 by txlab on July 9, 2013 - 12:22 pm

      you can try using the “originate” command — it can make a new call which will log into one conference and then bridge it with another… but it all needs careful planning and testing

      • #3 by Peter on July 9, 2013 - 12:27 pm

        Could you please explain a little bit more about it..
        I tried ‘conference_set_auto_outcall’ to connect to an extension. But that was not working.

    • #4 by txlab on July 9, 2013 - 1:48 pm

      come up in skype: ssinyagin, we can talk probably later today or tomorrow.

      • #5 by Peter on July 9, 2013 - 2:01 pm

        Thank you so much for your response and readiness to help.. Looking forward to hear from you later today..

  2. #6 by Saghar on September 13, 2013 - 9:06 am

    Thanks for a nice post. Can you please share the what is the best solution of the Peter’s problem. That will be very helpful for me and several other peoples. Thanks in advance.

    • #7 by txlab on September 13, 2013 - 9:08 am

      the best solution is to read the book, read the documentation and source code, and do the design work 🙂

      Honestly, you’re asking me to spend a few hours on describing the solution, and you’re not going to pay for my work, right?

  3. #8 by Gareth on January 30, 2014 - 6:21 pm

    Your conference.conf.xml is missing a closing tag:

    • #9 by txlab on January 30, 2014 - 10:30 pm

      alright, so that means someone reads it and understands it 🙂

      • #10 by Gareth on January 30, 2014 - 10:53 pm

        Wellll – certainly *reading* it 🙂

        Thank you so much for helping out clueless folk like me!

  4. #11 by Saigon Nguyen on April 9, 2014 - 2:24 am

    Hi there, I would like to mute all conference participants when they join by default with freeswitch and sipxecs 4.4. I reviewed mod_conference and under api reference it mentions conference mute [memberid|all|non moderatoes] ….. That seems greek to me, do you have an example of how i can execute an api reference? Or recommendations on how i can accomplish this? I am currently able to support 1076 users stable on my sipxecs and i was able to edit xml files to increase max sessions and turn of the enter and exit but this last one is greek to me.

%d bloggers like this: