Live voice streaming to azure using ARI

Hi folk,
I am a newbie to asterisk. I am trying to hit azure speech to text rest api using RTC to get live results of audio received. I am working on a java application. I am stuck where the datagram socket receive the packets.
Asterisk is throwing me with a error
Can someone please help me with this?
[Oct 20 22:20:48] WARNING[16790]: res_http_websocket.c:559 ws_safe_read: Web socket closed abruptly
[Oct 20 22:20:48] WARNING[16790]: ari/ari_websockets.c:126 ast_ari_websocket_session_read: WebSocket read error: Success
Deactivating Stasis app ‘my_stasis_app’
== WebSocket connection from ‘127.0.0.1:43900’ closed
Activating Stasis app ‘my_stasis_app’
== WebSocket connection from ‘127.0.0.1:52702’ for protocol ‘’ accepted using version ‘13’
– Executing [1000@from-internal:1] NoOp(“PJSIP/1001-0000001e”, “Starting Stasis App”) in new stack
– Executing [1000@from-internal:2] Answer(“PJSIP/1001-0000001e”, “”) in new stack
> 0x7fc3380d8ea0 – Strict RTP learning after remote address set to: 172.26.160.1:60848
> 0x7fc3380d8ea0 – Strict RTP switching to RTP target address 172.26.160.1:60848 as source
– Executing [1000@from-internal:3] Stasis(“PJSIP/1001-0000001e”, “my_stasis_app”) in new stack
> 0x7fc3a8002320 – Strict RTP learning after remote address set to: 127.0.0.1:9999
– Called 127.0.0.1:9999/c(slin16)
– UnicastRTP/127.0.0.1:9999-0x7fc3a800e640 answered
> Launching Stasis(1729443054.105) on UnicastRTP/127.0.0.1:9999-0x7fc3a800e640
[Oct 20 22:20:55] ERROR[16836]: res_stasis.c:1349 stasis_app_exec: Stasis app ‘1729443054.105’ not registered
> 0x7fc3380d8ea0 – Strict RTP learning complete - Locking on source address 172.26.160.1:60848

The error means what it says. You created an external media channel and told it to go to ARI application “1729443054.105” which doesn’t exist/isn’t registered. It can’t do that, so it doesn’t. As to whether it should exist or not - only you know your application details.

Hi jcolp,
I have registered my app with the name “my_stasis_app” but i dont know why it is pinging the app with channel id.

my java code,
public class AriRtpListener {

private static final String ARI_URL = "http://localhost:8088";
private static final String USERNAME = "jagadish";
private static final String PASSWORD = "Jagadish@123";
private static final String AZURE_URL =
        "https://centralindia.stt.speech.microsoft.com/speech/recognition/conversation/cognitiveservices/v1?language=en-US&format=detailed";
private static final String AZURE_SPEECH_KEY = "";
private static final ExecutorService executor = Executors.newFixedThreadPool(10);
private static final Logger logger = LoggerFactory.getLogger(AriRtpListener.class);

private ARI ari;

public static void main(String[] args) {
    new AriRtpListener().start();
}

/**
 * Initialize ARI connection and wait for incoming calls.
 */
public void start() {
    try {
        ari = ARI.build(ARI_URL, "my_stasis_app", USERNAME, PASSWORD, AriVersion.IM_FEELING_LUCKY);

        ari.eventsCallback(new ch.loway.oss.ari4java.generated.AriWSHelper() {
            @Override
            protected void onStasisStart(StasisStart event) {
                Channel channel = event.getChannel();
                logger.info("Call received: Channel ID - {}", channel.getId());
                executor.execute(() -> handleChannel(channel));
            }
        });

        logger.info("Waiting for calls...");
        Thread.sleep(Long.MAX_VALUE);  // Keep main thread alive
    } catch (Exception e) {
        logger.error("Error initializing ARI", e);
    }
}

/**
 * Handle the incoming channel by connecting external media and streaming RTP.
 */
private void handleChannel(Channel channel) {
    try {
        // Connect external media to the channel
        ari.channels().externalMedia(channel.getId(), "127.0.0.1:9999", "slin16").execute();
        logger.info("External media connected to channel {}", channel.getId());

        // Start receiving RTP packets and stream them to Azure
        receiveRTPPackets(channel.getId());
    } catch (ARIException e) {
        logger.error("Error connecting external media: {}", e.getMessage(), e);
    } catch (Exception e) {
        logger.error("Unexpected error in handleChannel: {}", e.getMessage(), e);
    }
}

/**
 * Receive RTP packets and stream them to Azure.
 */
private void receiveRTPPackets(String channelId) {
    logger.info("hit rtp");
    try (DatagramSocket socket = new DatagramSocket(9999, InetAddress.getByName("127.0.0.1"))) {
        logger.info("socket connected");
        byte[] buffer = new byte[2048];

        while (true) {
            DatagramPacket packet = new DatagramPacket(buffer, buffer.length);
            logger.info("Received packet data: {}", Arrays.toString(Arrays.copyOf(packet.getData(), packet.getLength())));

            socket.receive(packet);  // Receive RTP packet
            logger.info("received packet");
            // Strip off the RTP header (first 12 bytes)
            byte[] audioData = new byte[packet.getLength() - 12];
            System.arraycopy(packet.getData(), 12, audioData, 0, audioData.length);

            // Stream audio data to Azure
            streamAudioToAzure(audioData);
        }
    } catch (Exception e) {
        logger.error("Error receiving RTP packets: {}", e.getMessage(), e);
    }
}

/**
 * Stream audio data to Azure Speech-to-Text API.
 */
private void streamAudioToAzure(byte[] audioData) {
    try (CloseableHttpClient httpClient = HttpClients.createDefault()) {
        HttpPost request = new HttpPost(URI.create(AZURE_URL));

        // Set headers for Azure Speech-to-Text API
        request.setHeader("Ocp-Apim-Subscription-Key", AZURE_SPEECH_KEY);
        request.setHeader("Content-Type", "audio/wav; codecs=audio/pcm; samplerate=16000"); // Ensure this matches your audio format
        request.setHeader("Accept", "application/json;text/plain"); // Accept JSON response

        // Create the entity for the audio data
        InputStreamEntity entity = new InputStreamEntity(new ByteArrayInputStream(audioData), audioData.length);
        request.setEntity(entity);

        // Send the request and process the response
        String response = httpClient.execute(request, httpResponse -> {
            int statusCode = httpResponse.getStatusLine().getStatusCode();
            if (statusCode != 200) {
                String errorMessage = EntityUtils.toString(httpResponse.getEntity());
                logger.error("Error response from Azure: Status Code - {}, Message - {}", statusCode, errorMessage);
                return null; // Handle non-200 responses appropriately
            }
            return EntityUtils.toString(httpResponse.getEntity());
        });

        // Log the response from Azure
        if (response != null) {
            logger.info("Azure response: {}", response);
        } else {
            logger.error("Received an empty response from Azure.");
        }
    } catch (Exception e) {
        logger.error("Error streaming to Azure: {}", e.getMessage(), e);
    }
}

}

http.conf

[general]
servername=Asterisk
enabled=yes
bindaddr=0.0.0.0
bindport=8088
tlsenable=no
[ws]
enabled = yes
bindaddr = 0.0.0.0
bindport = 8088
[ari]
enabled=yes

ari.conf
[general]
enabled = yes
pretty = yes
[jagadish]
type = user
read_only = no
password = Jagadish@123

extensions.conf
[from-internal]
exten => 1001,1,Dial(PJSIP/1001)
exten => 1002,1,Dial(PJSIP/1002)

exten => 1000,1,NoOp(Starting Stasis App)
exten => 1000,n,Answer()
exten => 1000,n,Stasis(my_stasis_app)
exten => 1000,n,Hangup()

pjsip.conf
[transport-ws]
type = transport
protocol = ws
bind = 0.0.0.0:5060 ; Port for WebSocket connection

[transport-udp]
type=transport
protocol=udp ;udp,tcp,tls,ws,wss,flow
bind=0.0.0.0

[1001]
type=endpoint
transport=transport-udp
context=from-internal
disallow=all
allow=ulaw
auth=auth1001
aors=1001

[auth1001]
type=auth
auth_type=userpass
password=yourpassword
username=1001

[1001]
type=aor
max_contacts=1

[1002]
type=endpoint
transport=transport-udp
context=from-internal
disallow=all
allow=ulaw
auth=auth1002
aors=1002

[auth1002]
type=auth
auth_type=userpass
password=yourpassword2
username=1002

[1002]
type=aor
max_contacts=1

Please help me where i have gone wrong

Your problem is purely on your application side, so I would suggest looking at the arguments to the API calls in it. For example:

The first argument may be the ARI application to send the external media channel to.

Thank you so much jcolp, i tried it is throwing me with some other error will try to resolve it. Just confirm me whether am I going in the right direction for getting live stream data, or am i missing any asterisk feature?

External media is the method in ARI to send/receive media externally, and the method we recommend.

Thank you so much sir, wonderful community with wonderful people :sparkles:

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