How to pass Asterisk CALL_UUID from API server to Python AudioSocket app?

0

I have 2 Python services and an Asterisk dialplan setup:

api_server.py → exposes an HTTP API endpoint app.py → runs an AudioSocket server (accepts calls from Asterisk via TCP socket)

In my dialplan, I generate a CALL_UUID and notify the API server first, then start AudioSocket:

same  => n,Set(CALL_UUID=${UUID()})
same  => n,System(curl -s "http://192.168.1.6:8000/api/call-start?callerId=${CALLERID(num)}&uuid=${CALL_UUID}" >/dev/null 2>&1 || wget -q -O - "http://192.168.1.7:8000/api/call-start?callerId=${CALLERID(num)}&uuid=${CALL_UUID}" >/dev/null 2>&1)
same  => n,AudioSocket(${CALL_UUID},192.168.1.6:3000)

In api_server.py I successfully receive and store callerId + uuid (currently in memory/dict, but could also store in MongoDB).

The issue is in app.py → when the AudioSocket connection is accepted, I want to access the same uuid. But when I try to use conn.uuid, it is always None. If I try to recv().decode() the first bytes from the socket, I get an error because AudioSocket is sending raw audio (binary), not text metadata:

UnicodeDecodeError: 'utf-8' codec can't decode byte 0xbf in position 3: invalid start byte

My AudioSocket server looks like this:

class Audiosocket:
    def __init__(self, bind_info, timeout=None):
        if not isinstance(bind_info, tuple):
            raise TypeError("Expected tuple (addr, port), received", type(bind_info))

        self.addr, self.port = bind_info
        self.initial_sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
        self.initial_sock.bind((self.addr, self.port))
        self.initial_sock.settimeout(timeout)
        self.initial_sock.listen(1)
        self.port = self.initial_sock.getsockname()[1]

        self.user_resample = None
        self.asterisk_resample = None

    def prepare_output(self, outrate=44000, channels=2, ulaw2lin=False):
        self.asterisk_resample = audioop_struct(
            rate=outrate, channels=channels, ulaw2lin=ulaw2lin, ratecv_state=None
        )

    def listen(self):
        conn, peer_addr = self.initial_sock.accept()
        connection = Connection(conn, peer_addr, self.asterisk_resample)
        connection_thread = Thread(target=connection._process, args=())
        connection_thread.start()
        return connection

And I use it like this in app.py:

def listen_for_connections(self):
    try:
        conn = self.audiosocket.listen()
        log.debug(f"New AudioSocket connection received: {conn.uuid}")
        return conn
    except Exception as e:
        log.error(f"Error listening for connections: {e}")
        raise

So my questions are:

  1. How can I safely get the uuid from Asterisk into my app.py AudioSocket server?

  2. What’s the best design to handle concurrent calls safely (so one call doesn’t pick up another call’s UUID)?

Think of it the other way round: the server accepting the AudioSocket connection should be responsible for generating the UUID, and checking that the connection from Asterisk passes the matching UUID, to avoid confusion between different connections. The link between the two is in the dialplan.

I have a sample program illustrating this, as fastagi_audio_player_async in my Seaskirt examples repo. This runs as a FastAGI script, and accepts two kinds of requests.

The first kind of request generates a random UUID and also allocates a listening port address, and returns both in specified Asterisk channel variables.

The second is the actual AudioSocket request, which uses the previously-returned parameters to make the audio connection.

This topic was automatically closed 30 days after the last reply. New replies are no longer allowed.