Building a Command-Line SIP Dialer for Unix: Play Audio Files During Calls


3 views

In many automation and telephony integration scenarios, developers need a lightweight way to initiate SIP calls and play audio files without GUI dependencies. This is particularly useful for:

  • IVR system prototyping
  • Automated notification systems
  • Telephony integration in CI/CD pipelines
  • Batch calling operations

Here are three practical approaches to achieve this functionality:

1. Using pjsua from PJPROJECT

The most robust solution is using pjsua from the PJSIP library. Here's a sample bash script:

#!/bin/bash
PHONE_NUMBER=$1
AUDIO_FILE=$2

pjsua --id sip:user@your.sip.provider \
      --registrar sip:your.sip.provider \
      --username your_username \
      --password your_password \
      --realm '*' \
      --auto-play \
      --play-file=$AUDIO_FILE \
      sip:$PHONE_NUMBER

2. Python with SIPPY

For more programmatic control:

from sippy.SipCall import SipCall
import time

def make_call(number, audio_file):
    sip_acc = {
        'sip_user': 'username',
        'auth_user': 'authname',
        'pwd': 'password',
        'proxy': 'sip.provider.com'
    }
    
    call = SipCall(sip_acc)
    call.default_timeout = 30
    call.connect(number)
    
    if call.connected:
        call.play_wav(audio_file)
        time.sleep(10) # Wait for playback
        call.hangup()

make_call('+1234567890', 'message.wav')

3. Linphone Command Line

Linphone offers CLI capabilities with its linphonec tool. First create a configuration file (~/.linphonerc):

[sip]
sip_listen_port=5060
reg_proxy=sip:your.provider.com
reg_identity=sip:user@your.provider.com
reg_expires=3600
reg_password=yourpassword

Then create an expect script to automate the call:

#!/usr/bin/expect -f

set number [lindex $argv 0]
set audio [lindex $argv 1]

spawn linphonec
expect "linphonec> "
send "play $audio\n"
expect "linphonec> "  
send "call $number\n"
expect "Call with .* established"
sleep 10
send "terminate\n"
expect "linphonec> "
send "quit\n"

When implementing SIP dialing, consider these aspects:

  • Codec compatibility - Ensure your audio file matches supported codecs (PCMA/PCMU for G.711, GSM, etc.)
  • Authentication - Some providers require specific auth methods
  • DTMF support - If you need to send touch tones after connection
  • Error handling - Network issues, busy signals, etc.

Here's how to implement a Python solution with async support using asyncio and aiosip:

import asyncio
import aiosip
from aiosip.media import RTPEndpoint, WavePlayer

async def make_call():
    sip_config = {
        'srv_host': 'sip.provider.com',
        'srv_port': 5060,
        'user': 'username',
        'pwd': 'password'
    }

    peer = await aiosip.Peer.create(
        local_addr=('0.0.0.0', 5060),
        remote_addr=(sip_config['srv_host'], sip_config['srv_port'])
    )

    dialog = await peer.invite(
        from_details=aiosip.Contact.from_header(f'sip:{sip_config["user"]}@{sip_config["srv_host"]}'),
        to_details=aiosip.Contact.from_header('sip:+1234567890@sip.provider.com')
    )

    if dialog.state == 'connected':
        rtp = RTPEndpoint(dialog)
        player = WavePlayer('message.wav')
        rtp.connect(player)
        await asyncio.sleep(10) # Play duration
        await dialog.bye()
    
    peer.close()

asyncio.run(make_call())

For production systems:

  • Use SRTP for secure media transmission
  • Implement proper logging (SIP messages, call status)
  • Add retry logic for transient failures
  • Consider using a SIP ALG if behind NAT

When working with SIP telephony in Unix environments, we often encounter scenarios that demand automated calling capabilities - whether for notification systems, IVR testing, or batch call processing. The ideal solution should combine SIP connectivity with media playback in a lightweight command-line package.

After extensive testing, these CLI tools stand out for SIP calling with media support:

1. pjsua - Comes with PJSUA CLI interface
2. sipcmd - Lightweight SIP command line tool
3. linphonec - Console version of Linphone
4. baresip - Modular SIP client with CLI

The sipcmd utility provides the most straightforward approach for our use case. Here's how to install and use it:

# Installation on Debian/Ubuntu
sudo apt install sipcmd

# Basic call with audio playback
sipcmd -u username -p password -P sip.provider.com \
       -c "dtmf 1234; play /path/to/message.wav; wait 20; hangup" \
       -w -x "c 18005551234"

For more complex scenarios, we can create scriptable solutions:

#!/bin/bash
PHONE="+18005551234"
AUDIO_FILE="/opt/ivr/greeting.wav"
SIP_SERVER="sip.example.com"
SIP_USER="1001"
SIP_PASS="secret"

sipcmd -u $SIP_USER -p $SIP_PASS -P $SIP_SERVER \
       -c "play $AUDIO_FILE; wait 30; hangup" \
       -w -x "c $PHONE"

For more programmatic control, PJSUA offers Python integration:

import pjsua

lib = pjsua.Lib()
lib.init()
lib.start()

acc = lib.create_account(pjsua.AccountConfig(
    "sip.provider.com", "username", "password"))

call = acc.make_call("sip:18005551234@sip.provider.com", 
                    cb=MyCallCallback())

# In callback class:
def on_media_state(self):
    if self.lib.media_active:
        player = self.lib.create_player("message.wav")
        self.lib.connect_player_to_call(player, self.call)
  • Codec mismatches - Ensure your SIP server and client support compatible codecs (PCMA/PCMU for WAV files)
  • Firewall settings - SIP uses 5060 UDP/TCP by default
  • Audio file format - Convert to 8kHz/16bit mono WAV for best compatibility

When processing large call volumes:

  • Use ulimit to increase file descriptors
  • Consider separate processes for concurrent calls
  • Monitor CPU usage with top/htop during calls