openwrt上Asterisk系统语音信箱时间不对问题分析

      最近在openwrt上搭建了Asterisk,配合fxo语音网关,内线转外线,外线转内线,十分方便。但在微信这么流行,长途也取消漫游费的情况下,这个完全成了屠龙术,无用武之地。偶然发现Asterisk的语音信箱系统十分完善,给家里的固定电话加个语音留言功能,这个还算有些使用场景,折腾了几天把一个语音信箱系统搭建起来了,支持电话无人接听转语音信箱,支持密码,支持不同的家庭成员用不同的号码收听语音留言,以下是过程纪录:

    先要在voicemail中配置一个账号,类似于sip.conf中配置号码一样

[myvm]
home =>, HOME,,,tz=eastern

   

然后把中文语音包替换原始的,中国人当然要听中文,感谢翻译人员的默付出

本文附件中的是根据上述翻译包与openwrt默认的语音文件合并后,绝大部分语音都是中文,点击下载》》》》》》下载链接

  asterisk默认的语音留言目录是var/spool/asterisk,这个目录是软链接,实际整个var目录都是映射到了/tmp目录,重启就丢失语言留言数据了,真坑。

需要修改如下两处:

1) /etc/init.d/asterisk
  dbdir=/var/lib/asterisk/astdb
  logdir=/var/log/asterisk
  cdrcsvdir=$logdir/cdr-csv
  rundir=/var/run/asterisk
  spooldir=/mnt/sdb/asterisk ##修改为自己的目录
  varlibdir=/var/lib/asterisk

2)/etc/asterisk/asterisk.conf
[directories]
astcachedir => /tmp
astetcdir => /etc/asterisk
astmoddir => /usr/lib/asterisk/modules
astvarlibdir => /var/lib/asterisk
astdbdir => /var/lib/asterisk
astkeydir => /var/lib/asterisk
astdatadir => /usr/share/asterisk
astagidir => /usr/share/asterisk/agi-bin
astspooldir => /mnt/sdb/asterisk   ;修改为自己的目录
astrundir => /var/run/asterisk
astlogdir => /var/log/asterisk
astsbindir => /usr/sbin

   最后加个extension,设置呼叫多长时间转语音信箱,内外线用不同的号码访问语音信箱,收听留言

/etc/asterisk/extensions.conf

;for fix phone, redirct to voicemail for 40s timeout
exten => 1002,1,Answer()
exten => 1002,n,Dial(SIP/1002,40,tr) ;拨打1002分机40秒无人接听就转语音留言
exten => 1002,n,VoiceMail(home@myvm)
exten => 1002,n,Hangup

;for reading voicemail
exten => 8,1,Answer()
exten => 8,n,VoiceMailMain(home@myvm);拨打分机8,即可收听语音信箱的留言
exten => 8,n,Hangup()

   这些都做完后,试试看吧,外线进来的电话,没人接听就语音提示留言,拨打分机8即可收听留言,还会提示有多少个新留言,多少个旧留言,还是挺方便的,这种业务在运营商那都是收费的。遥想当年大学宿舍八个兄弟,大冬天熄灯躲被窝后,来电话谁也不想下床接,要有个这玩意,接个音箱听着妹子柔柔的声音说找哪个哥哥,要多fasion有多fasion。

        就这样,跑了两天,发现一个bug,收听语音信箱时,妹子播报的时间不对,差了8个小时,尽管不太影响什么,但有点强迫症的,就想搞明白为什么,因为ubuntu上同样的方式搭建的同版本asterisk就没问题。

           一番搜索,尝试打开这个文件,发现时间是UTC时间,是对的,那基本就想到是时区的问题

/var/spool/asterisk\voicemail\xxx\xxx\Old\msg0000.txt

;
; Message Information file
;
[message]
origmailbox=home
context=public
macrocontext=
exten=5
rdnis=unknown
priority=6
callerchan=SIP/1005-00000000
callerid=1005
origdate=Tue Nov  9 04:01:09 PM UTC 2021
origtime=1636473669
category=
msg_id=1636473669-00000000
flag=
duration=4

   openwrt的时区在如下文件中配置,检查过了,也是对的,那就奇怪了

/etc/config/system

config system
        option ttylogin '0'
        option log_size '64'
        option urandom_seed '0'
        option hostname 'xxxxx'
        option log_proto 'udp'
        option conloglevel '8'
        option cronloglevel '8'
        option zonename 'Asia/Shanghai'
        option timezone 'CST-8'

config timeserver 'ntp'
        option enabled '1'
        list server 'ntp.aliyun.com'
        list server 'time1.cloud.tencent.com'
        list server 'time.ustc.edu.cn'
        list server 'cn.pool.ntp.org'

查看asterisk CLI的输出,有如下把文本转乘语音播放的过程,看样子,原始的时间就是错的,导致播放出来的时间就是错误的

 -- Executing [8@public:2] VoiceMailMain("SIP/1005-00000001", "home@myvm") in new stack
    -- <SIP/1005-00000001> Playing 'vm-youhave.slin' (language 'en')
    -- <SIP/1005-00000001> Playing 'digits/1.slin' (language 'en')
    -- <SIP/1005-00000001> Playing 'vm-INBOX.slin' (language 'en')
    -- <SIP/1005-00000001> Playing 'vm-and.slin' (language 'en')
    -- <SIP/1005-00000001> Playing 'digits/11.slin' (language 'en')
       > 0x15304c0 -- Strict RTP learning complete - Locking on source address 36.112.201.111:22155
    -- <SIP/1005-00000001> Playing 'vm-Old.slin' (language 'en')
    -- <SIP/1005-00000001> Playing 'vm-messages.slin' (language 'en')
    -- <SIP/1005-00000001> Playing 'vm-onefor.slin' (language 'en')
    -- <SIP/1005-00000001> Playing 'vm-INBOX.slin' (language 'en')
    -- <SIP/1005-00000001> Playing 'vm-messages.slin' (language 'en')
    -- <SIP/1005-00000001> Playing 'vm-opts.slin' (language 'en')
    -- <SIP/1005-00000001> Playing 'vm-first.slin' (language 'en')
    -- <SIP/1005-00000001> Playing 'vm-message.slin' (language 'en')
    -- <SIP/1005-00000001> Playing 'vm-received.slin' (language 'en')
    -- <SIP/1005-00000001> Playing 'digits/today.slin' (language 'en')
    -- <SIP/1005-00000001> Playing 'digits/at.slin' (language 'en')
    -- <SIP/1005-00000001> Playing 'digits/3.slin' (language 'en')
    -- <SIP/1005-00000001> Playing 'digits/30.slin' (language 'en')
    -- <SIP/1005-00000001> Playing 'digits/7.slin' (language 'en')
    -- <SIP/1005-00000001> Playing 'digits/p-m.slin' (language 'en')
    -- <SIP/1005-00000001> Playing '/var/spool/asterisk/voicemail/myvm/home/INBOX/msg0000.slin' (language 'en')
    -- <SIP/1005-00000001> Playing 'vm-advopts.slin' (language 'en')
    -- <SIP/1005-00000001> Playing 'vm-repeat.slin' (language 'en')
    -- <SIP/1005-00000001> Playing 'vm-delete.slin' (language 'en')

其中时间相关的如下:

 查看asterisk的代码发现,在app_voicemail.c中实现

static int play_message(struct ast_channel *chan, struct ast_vm_user *vmu, struct vm_state *vms)
这个函数里面包含了从/var/spool/asterisk/voicemail/xxxx/xxx/INBOX/msg0000.txt中取“origtime”字段的时间,然后调用下面的函数,传入origtime


static int play_message_datetime(struct ast_channel *chan, struct ast_vm_user *vmu, const char *origtime, const char *filename)

vmu 是voicemail user的意思;可以用CLI命令:voicemail show users命令查看

obana*CLI> voicemail show users
Context    Mbox  User                      Zone       NewMsg
default    1234  Example Mailbox           eastern         0
myaliases  1234@devices                           eastern         0
other      1234  Company2 User             eastern         0
myvm       xxxx   xxxx                     eastern         0
myvm       xxxx   xxxx                     eastern         0
5 voicemail users configured.

其中eastern指向了voicemail.conf中的[zonemessages]配置,可以通过如下命令查看

obana*CLI> voicemail show zones
Zone            Timezone             Message Format
european        Europe/Copenhagen    'vm-received' a d b 'digits/at' HM
military        Zulu                 'vm-received' q 'digits/at' H N 'hours' 'phonetic/z_p'
central24       America/Chicago      'vm-received' q 'digits/at' H N 'hours'
central         America/Chicago      'vm-received' Q 'digits/at' IMp
eastern         Asia/Shanghai        'vm-received' Q 'digits/at' IMp

我配置的是Asia/Shanghai,貌似也没问题,接着往下分析:

进一步看代码,播放这个时间的逻辑如下:

static int say_date_with_format(struct ast_channel *chan, time_t t, const char *ints, const char *lang, const char *format, const char *tzone)

进一步调用
int ast_say_date_with_format_en(struct ast_channel *chan, time_t t, const char *ints, const char *lang, const char *format, const char *tzone)

里面有个关键的函数:
ast_localtime(&when, &tm, tzone);

分析ast_localtime这个函数在localtime.c中实现,其功能就是把UTC时间转成本地时间,里面关键的产生就是第三个tzone,在我的Asteris上,这个参数是正确的,那毫无疑问,这个函数内部出问题了,基本应该是找到了根源,进一步分析:

最终会走到这几行代码中,tzload是加载sp,sp是关键的时间转换函数,跟zone是强相关的
if (tzload(zone, sp, TRUE) != 0) {
		if (zone[0] == ':' || tzparse(zone, sp, FALSE) != 0)
			(void) gmtload(sp);
	}


再往下看
static int tzload(const char *name, struct state * const sp, const int doextend)
这个函数就是从linux中加载时区信息,用到了tzhead数据结构,相信这个函数逻辑不会有问题,毕竟Asterisk也发展多年,所以一眼就看到了如下代码:
		if (!doaccess) {
			if ((p = TZDIR) == NULL)
				return -1;
			if ((strlen(p) + strlen(name) + 1) >= sizeof fullname)
				return -1;
			(void) strcpy(fullname, p);
			(void) strcat(fullname, "/");
			(void) strcat(fullname, name);
			
上述代码实现了拼接fullname,fullname指向了linux上的时区文件,正常的应该如下:
/usr/share/zoneinfo/Asia/Shanghai
我的openwrt上居然没有这个文件

tzfile.h定义如下:看样子是linux标准的

#ifndef TZDIR
#ifdef SOLARIS
#define TZDIR	"/usr/share/lib/zoneinfo"
#else
#define TZDIR	"/usr/share/zoneinfo"
#endif /* defined SOLARIS */
#endif /* !defined TZDIR */

ubuntu18.04上发现有这个文件,centos上也有,armbian上也有,ESXI上没有

本想拷贝一份到openwrt上,怕格式不兼容,搜索了一番,安装个包就行了
opkg install zoneinfo-asia

到这,貌似就解决了,重启了asterisk,居然不行,果断重启openwrt,一切正常了,分析结束。

经验:openwrt近几年很活跃,但asterisk多年前的产物了,估计当年跑openwrt的设备CPU能力还不足以跑asterisk,作者也没太考虑arm设备,但随着arm处理器的提升,搞个家庭、小型企业用途的开源sip服务,asterisk还是挺胜任的,但目前openwrt上的asterisk还是有不少问题,可以考虑移植搞个小系统。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值