You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何使用AsterNET.ARI实现Asterisk内部号码间的简单呼叫?

AsterNET.ARI 实现内部号码401呼叫402失败问题排查与解决

问题场景

需通过AsterNET.ARI实现绑定软电话的内部号码401呼叫402,当前通过拨打1000触发Stasis应用,但调用Channels.Originate后呼叫未发起,且缺少官方示例参考。

当前使用的extensions.conf配置:

exten => 1000,1, NoOp()
same =>  n,Stasis(originate-example)
same =>  n,Hangup()

原C#代码存在参数和逻辑错误,导致呼叫失败。


问题原因分析

  1. 参数缺失:原Originate调用未指定context参数,Asterisk无法确定在哪个拨号计划上下文查找目标号码402的路由
  2. 逻辑错误:直接指定endpoint: "PJSIP/401"和extension: "402"不符合ARI呼叫逻辑——ARI的Originate是创建新通道呼叫指定终端,而非让指定终端主动发起呼叫
  3. 事件覆盖不全:仅订阅StasisStartEvent,未处理通道接听、桥接创建等关键环节,无法完成完整呼叫流程

解决方案

1. 调整extensions.conf配置

补充内部号码路由,确保Asterisk能正确解析4XX号码:

[internal]
; 内部号码路由:匹配4XX号码,直接拨打对应PJSIP终端
exten => _4XX,1,Dial(PJSIP/${EXTEN})
; 触发Stasis应用的入口号码
exten => 1000,1,NoOp()
same => n,Stasis(originate-example)
same => n,Hangup()

2. 修正C#代码

实现完整呼叫流程:先呼叫401,待其接听后呼叫402,最后通过桥接关联两个通道

using AsterNET.ARI;
using AsterNET.ARI.Helpers;
using AsterNET.ARI.Models;
using System;

var AppName = "originate-example";
var actionClient = new AriClient(new StasisEndpoint("127.0.0.1", 8088, "asterisk", "asterisk"), AppName);

// 订阅关键事件
actionClient.OnStasisStartEvent += OnStasisStart;
actionClient.OnChannelAnswerEvent += OnChannelAnswer;
actionClient.OnStasisEndEvent += OnStasisEnd;

actionClient.Connect();
Console.WriteLine("Stasis应用已启动,按回车退出");
Console.ReadLine();
actionClient.Disconnect();

// 处理Stasis启动事件
void OnStasisStart(IAriClient sender, StasisStartEvent e)
{
    // 初始触发通道(拨打1000的通道):接听并发起对401的呼叫
    if (e.Channel.Dialplan.Extension == "1000")
    {
        sender.Channels.Answer(e.Channel.Id);
        SyncHelper.Wait(sender.Channels.Play(e.Channel.Id, "sound:vm-dialout", "en", 0, 0, Guid.NewGuid().ToString()), sender);
        
        // 发起对401的呼叫,接听后进入Stasis应用
        var originateResponse = SyncHelper.Wait(sender.Channels.Originate(
            endpoint: "PJSIP/401",
            callerId: "自动拨号",
            app: AppName,
            timeout: 30000
        ), sender);
        
        Console.WriteLine($"已发起对401的呼叫,通道ID: {originateResponse.Id}");
    }
    // 401的通道进入Stasis:播放提示音
    else if (e.Channel.Name.Contains("401"))
    {
        SyncHelper.Wait(sender.Channels.Play(e.Channel.Id, "sound:calling", "en", 0, 0, Guid.NewGuid().ToString()), sender);
    }
}

// 处理通道接听事件
void OnChannelAnswer(IAriClient sender, ChannelAnswerEvent e)
{
    // 401接听后,发起对402的呼叫并创建桥接
    if (e.Channel.Name.Contains("401"))
    {
        var originate402Response = SyncHelper.Wait(sender.Channels.Originate(
            endpoint: "PJSIP/402",
            callerId: "401",
            app: AppName,
            timeout: 30000
        ), sender);
        
        // 创建混音桥接
        var bridge = SyncHelper.Wait(sender.Bridges.Create("mixing", Guid.NewGuid().ToString()), sender);
        
        // 将两个通道加入桥接
        SyncHelper.Wait(sender.Bridges.AddChannel(bridge.Id, new[] { e.Channel.Id }), sender);
        SyncHelper.Wait(sender.Bridges.AddChannel(bridge.Id, new[] { originate402Response.Id }), sender);
        
        Console.WriteLine("桥接创建成功,401与402已建立通话");
    }
}

// 处理Stasis结束事件:清理桥接资源
void OnStasisEnd(IAriClient sender, StasisEndEvent e)
{
    var bridges = SyncHelper.Wait(sender.Bridges.List(), sender);
    foreach (var b in bridges)
    {
        if (b.Channels.Contains(e.Channel.Id))
        {
            SyncHelper.Wait(sender.Bridges.Destroy(b.Id), sender);
            Console.WriteLine("桥接已销毁");
            break;
        }
    }
}

关键注意事项

  • 确保401、402的PJSIP终端已正确配置并在线
  • Asterisk需启用ARI模块,ari.conf中已配置合法的访问用户与权限
  • 调整timeout参数为合理值(如30000毫秒),避免因超时导致呼叫失败

内容的提问来源于stack exchange,提问作者Александр Инженер

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.27 22:53:21