Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

No communication between local server and Asterisk in container #20

Open
gtjoseph opened this issue Aug 4, 2023 · 7 comments
Open

No communication between local server and Asterisk in container #20

gtjoseph opened this issue Aug 4, 2023 · 7 comments

Comments

@gtjoseph
Copy link
Member

gtjoseph commented Aug 4, 2023

@gtjoseph Could the Asterisk version be a plausible cause? I looked through the changelog but couldn't find any ARI related breaking changes. In my case, everything seems to be fine but the audio server running locally (host) just doesn't receive any packages from Asterisk 20 running within Docker.

Originally posted by @jneuendorf-i4h in #11 (comment)

@gtjoseph
Copy link
Member Author

gtjoseph commented Aug 4, 2023

Can you run Asterisk on the host just as a test and see if it works?
What kind of networking are you using with docker?
Can you connect to asterisk's built-in web server from a browser on the host?

@jneuendorf-i4h
Copy link

First of all, thanks for the quick response! 🙏

Can you run Asterisk on the host just as a test and see if it works?

My setup is on macOS and I am not sure if Asterisk can be installed on macOS as easily as on Debian/Ubuntu.
Therefore, I have not tried this approach yet.

What kind of networking are you using with docker?

So far, I stuck with the defaults. With network_mode: host (docker-compose.yml) phone calls no longer worked.

Can you connect to asterisk's built-in web server from a browser on the host?

  1. Yes, on my 1st attempt, I started the ARI client on the host and the connection to the REST API worked (authentication, channel creation etc.)
  2. 2nd attempt: Because I suspected networking problems due to Docker, I started the ARI client from within docker (so everything on localhost/127.0.0.1 within Docker. But the behavior stayed the same. 🤔

I actually simplified/adjusted this repo's code for the start, as I want to initiate a call to an SIP device (PJSIP/6001) and stream the audio from/to the audio server (without any processing for now).

Any advice or hint would be very helpful, so let me know if anything is unclear or if there is information missing.
Also, debugging tips are very welcome (so far, I've only used ari set debug all on). 😉

For better insights, here my code:

Client

src/upd.mjs

Start the audio server in a separate script to reduce complexity in the main script (for debugging).

import { RtpUdpServerSocket } from "../lib/rtp-udp-server.js"

const audioServer = new RtpUdpServerSocket(
    '0.0.0.0:9999',
    false,
    "./test.wav",
)
console.log(audioServer)

src/send.mjs

Send a test message to the audio server.

import dgram from 'node:dgram'
import { Buffer } from 'node:buffer'

const message = Buffer.from('A few bytes to send for testing purposes...')
const client = dgram.createSocket('udp4')
client.send(message, 9999, 'localhost', (err) => {
    console.log('error', err)
    client.close()
})

src/main.mjs

Start the ARI application, setup the channels and bridge, and make the call.

import ari from 'ari-client'

const options = {
    dialstring: "PJSIP/6001",
    format: "ulaw",
    listenServer: '127.0.0.1:9999',
    stasisApp: "external-media",
}

ari.connect(
    'http://127.0.0.1:8088',
    'asterisk',
    'asterisk',
    async (error, client) => {
        if (error) {
            console.error(error)
            console.warn("CLOSING")
        }

        await client.start(options.stasisApp)

        // Create a simple mixing bridge that is controlled by ARI/Stasis
        const mixingBridge = client.Bridge()
        try {
            await mixingBridge.create({type: "mixing"})
            console.log("created mixing bridge")
        } catch(error) {
            console.error(error)
            console.warn("CLOSING")
        }
        mixingBridge.on('BridgeDestroyed', (event) => {
            console.log("destroyed mixing bridge")
            console.warn("CLOSING")
        })

        const outboundChannel = client.Channel()
        outboundChannel.on('StasisStart', (event, {id}) => {
            mixingBridge.addChannel({channel: id})
            console.log("added outbound channel to mixing bridge", id)
        })
        outboundChannel.on('StasisEnd', (event, chan) => {
            console.warn("CLOSING")
        })

        // Call the phone or confbridge specified in dialstring
        try {
            await outboundChannel.originate({
                endpoint: options.dialstring,
                formats: options.format,
                app: options.stasisApp,
            })
            // localChannel.dial()
        } catch (error) {
            console.error(error)
            console.warn("CLOSING")
        }

        // Now we create the External Media channel.
        const externalChannel = client.Channel()
        externalChannel.on('StasisStart', (event, {id}) => {
            mixingBridge.addChannel({channel: id})
            console.log("added external channel to mixing bridge", id)
        })
        externalChannel.on('StasisEnd', (event, chan) => {
            console.warn("CLOSING")
        })

        /*
         * We give the external channel the address of the listener
         * we already set up and the format it should stream in.
         */
        const externalMediaChannel = await externalChannel.externalMedia({
            app: options.stasisApp,
            // external_host: options.listenServer,
            external_host: "127.0.0.1:9999",
            format: options.format,
        })
        console.log("externalMediaChannel", externalMediaChannel)

        setTimeout(async () => {
            console.warn("STOPPING")
            outboundChannel && await outboundChannel.hangup()
            externalChannel && await externalChannel.hangup()
            mixingBridge && await mixingBridge.destroy()
            client.stop()
        }, 30 * 1000);
})

Docker

docker-compose.yml

version: '3'
services:
  asterisk:
    image: asterisk
    build:
      context: .
      dockerfile: docker/asterisk/Dockerfile
    # network_mode: host
    ports:
      - "8088:8088"                   # ARI HTTP
      - "5060:5060/udp"               # SIP UDP
      - "5060:5060"                   # SIP TCP
      - "5061:5061"                   # SIP TLS
      - "10000-10099:10000-10099/udp" # RTP
      - "9999:9999/udp" # test stream from client on host to audio server in Docker

Server

ari.conf

[general]
enabled = yes
pretty = yes
allowed_origins = *

[asterisk]
type = user
read_only = no
password = asterisk

http.conf

[general]
enabled = yes
bindaddr = 0.0.0.0
bindport = 8088

[asterisk]
type = user
read_only = no
password = asterisk

rtp.conf

[general]
rtpstart = 10000
rtpend   = 10099

strictrtp = no

extensions.conf

[from-internal]
exten => 100,1,Answer()
 same => n,Stasis(external-media)
 same => n,Hangup()

pjsip.conf

[transport-udp]
type=transport
protocol=udp
bind=0.0.0.0

[6001]
type=endpoint
context=from-internal
disallow=all
allow=ulaw
auth=6001
aors=6001

[6001]
type=auth
auth_type=userpass
password=unsecurepassword
username=6001

[6001]
type=aor
max_contacts=1

@mattbonnell
Copy link

seeing the same thing here; listening on port 9999 with netcat but doesn't seem anything is coming through.

@vlxdisluv
Copy link

have you solved you problem? @jneuendorf-i4h

@jneuendorf-i4h
Copy link

Thanks for catching up @vlxdisluv! The currently working solution is running Asterisk not in a Docker container but in a (Proxmox) VM (LXC untested).

A reasonable cause is network_mode: host on macOS:

The host networking driver only works on Linux hosts, and is not supported on Docker Desktop for Mac
https://docs.docker.com/network/drivers/host/

So I guess, the issue can be closed, even though it would be helpful to have documentation for running Asterisk in Docker (on specific machines/operating systems).

@vlxdisluv
Copy link

vlxdisluv commented Mar 20, 2024

@jneuendorf-i4h I have successfully configured Asterisk in Docker container (macOS m1 arm) because it can establish a peer-to-peer connection (between two softphones), but I don't have enough knowledge to write a proper Asterisk configuration for a workflow with external media. I need to initiate an outgoing ARI call to a softphone and receive real-time RTP packets and then send them to some STT provider. In this case, I also established a connection, but I cannot receive RTP packets. Could you share your working Asterisk configs?

@jneuendorf-i4h
Copy link

@jneuendorf-i4h I have successfully configured Asterisk in Docker container (macOS m1 arm) because it can establish a peer-to-peer connection (between two softphones), but I don't have enough knowledge to write a proper Asterisk configuration for a workflow with external media. I need to initiate an outgoing ARI call to a softphone and receive real-time RTP packets and then send them to some STT provider. In this case, I also established a connection, but I cannot receive RTP packets. Could you share your working Asterisk configs?

Have you checked the audio signal of your p2p/softphone connection? I would not be surprised if the connection can be established while not receiving RTP packets. RTP debugging can be done via several different approaches, e.g.

  • pjsip set logger on
  • rtp set debug on
  • tcpdump -i <INTERFACE> -w dump.pcap + Wireshark (open file, telephony > VOIP calls: shows all call legs > flow)

On the JS client side, you could try (using the nomenclature from above)

setInterval(async () => {
    const rtpStats = await outboundChannel.rtpstatistics()
    console.log("RTP", rtpStats)
}, 5000)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

4 participants