Asterisk播放mp4(3)——搭建开发环境

Asterisk现有版本不支持播放视频文件(支持视频通话),无法满足发送视频通知、视频IVR等场景。本系列文章,通过学习音视频的相关知识和工具,尝试实现一个通过Asterisk播放mp4视频文件的应用。

Asterisk是一个开源SIP应用服务器,支持多种方式进行扩展。本文介绍如何利用Docker搭建Asterisk的本地开发测试环境,并且提供一些利用docker简化开发过程的技巧。因为本系列文章要通过ffmpeg提供媒体处理能力,所以会涉及如何整合Asterisk和ffmpeg的内容。

项目地址:https://github.com/jasony62/tms-asterisk-dev

准备Docker环境

下载需要版本的asterisk-13.33.0源代码压缩包,官网下载地址

建立Dockerfile文件,制作基于centos7环境的镜像。为了跳过ffmpeg的安装(从源代码安装ffmpeg真的很麻烦),以jrottenberg/ffmpeg:4.2.3-centos7为基础开始构建。

在镜像构建阶段完成asterisk的编译和安装,建立build-asterisk.sh文件将编译和安装过程从Dockerfile中独立出来。执行过程中以menuselect命令行方式指定安装选项。

安装过程中需要使用第三方库janssonpjproject,会在编译阶段进行下载,但是因为网络问题(https://raw.githubusercontent.com能否正常访问)会下载失败,导致无法安装。为了解决这个,先把这两个包下载到本地。然后修改asterisk源代码包中的third-party目录中这个两个项目的Makefile.rules文件(用环境变量HOST_DOMAIN指定主机地址,因为是在容器中运行的,不能写成localhost127.0.0.1),将下载文件的地址指向本地。为了实现在本地下载文件,执行npm i http-server -g安装http服务器(如果需要),在存放下载文件的目录下,执行http-server -p 80启动文件下载服务。

我们要在自定义的asterisk应用中使用ffmpeg,因此需要修改编译选项。在源代码目录中打开main/Makefile,添加需要的动态链接库。(注意:这里修改后需要重新构建镜像。)

AST_LIBS+=-lavformat -lavcodec -lavutil -lswresample -lswscale -lavfilter

做完上述准备工作就可以开始构建镜像,运行容器了。

  • 构建镜像

docker-compose -f docker-compose.13.yml build

  • 启动容器

docker-compose -f docker-compose.13.yml up

  • 进入容器

docker exec -it tms-asterisk_13.33.0 bash

  • 结束容器(启动后复制到容器中的内容会丢失,如果直接ctrl+c关闭会保留)

docker-compose -f docker-compose.13.yml down

设置Asterisk

Asterisk支持通过配置文件定制系统的行为,例如:使用extensions.conf指定拨号计划等。在docker-compose.13.yml文件中,将本地配置文件和容器中的文件建立了关联,这样修改本地文件后,在容器中重启asterisk服务就可以了。

另外,在开发测试时经常需要查看日志,因此通过docker-compose.13.yml文件,将asterisk的日志目录指向了本地,便于查看日志输出内容。

Asterisk处理媒体时需要动态提供RTP端口,因为docker在windows和mac环境中不支持设置为host模式,需要在docker-compose.13.yml文件中指定开放的端口(和配置文件rtp.conf中的设置保持一致。)。

Asterisk运行在容器中,其地址无法访问,这样会导致媒体协商中指定的地址无法访问,需要在pjsip.conf文件中将external_media_addressexternal_signaling_address指定为宿主机地址。

Asterisk常用命令

  • 重启asterisk。不需要用命令asterisk -r进入asterisk后再执行core restart now,两步变一步。

asterisk -rx "core restart now"

  • 截断日志。asterisk输出的日志非常多,为了便于查看经常需要在输出前清空之前的日志。

asterisk -rx "logger rotate"

  • 打开pjsip的日志。

asterisk -rx "pjsip set logger on"

  • 更新dialplan

asterisk -rx "dialplan reload"

开发应用

docker-compose.13.yml文件中,通过volumns指令将自定义应用文件关联到Asterisk源代码的apps目录下(/usr/src/asterisk/apps)。每次在外部修改代码后,到容器中执行make && make install,重启asterisk。

Asterisk在代码中提供了三种输出日志ast_logast_verbast_debug的方法,通过配置文件logger.conf进行设置。可以将通过配置将不同的信息(类型和等级)输出到不同文件。ast_verb支持设置level控制输出的内容,可以在启动时指定asterisk -vvv,也可以通过命令指定core set verboseast_debug支持设置level控制输出的内容,可以在启动时指定asterisk -ddd,也可以通过命令指定core set debuglevel的设置没有限制。

播放alaw裸流文件

  • 设置帧类型和采样编码格式
f->frametype = AST_FRAME_VOICE;
f->subclass.format = ast_format_alaw;
  • 设置采样数据
f->samples = nb_samples;
uint8_t *data;
data = AST_FRAME_GET_BUFFER(f);
memcpy(data, output_data, nb_samples);
f->datalen = nb_samples;
  • 帧间添加延时
duration = (int)(((float)nb_samples / (float)ALAW_SAMPLE_RATE) * 1000 * 1000);
ast_debug(2, "完成第 %d 个RTP帧发送,添加延时 %d\n", nb_rtps, duration);
usleep(duration);
  • 对比wireshark抓包数据和alaw文件中的数据
sine-8k-10s.alaw
wireshark抓包

可以看到数据是一致的,说明我们通过asterisk正确地发送了数据。

播放mp3文件

Asterisk不支持mp3编码,我们通过ffmpeg进行转码。

音频处理流程

ffmpeg是一个媒体编处理框架,整合非常多的编解码库,并且对媒体加工过程进行了抽象,形成了一个统一的处理流程。我理解,处理流程大体上分为5个阶段:

  1. 初始化阶段。读取文件中的信息,准备好编码器,解码器,重采样器等。
  2. 解码阶段。从文件中读取编码包(AVPacket),将编码包发送解码器,从解码器中接收未压缩的原始帧(AVFrame)。
  3. 重采样阶段。每种音频编码格式的原始帧采样格式(sample_fmt)不一样,例如:mp3的采样格式为fltp,alaw的采样格式为s16(我理解,采样格式是没有压缩的采样数据的记录方式,alaw对应的是13位数据,所以没有压缩的状态就需要用s16记录)。为了进行编码转换,首先需要编码格式的转换,这个过程叫做重采样。
  4. 编码阶段。将采样数据帧(AVFrame)发送给编码器,从编码器中获得编码后的包(AVPacket),通过astersik发送rtp包。
  5. 清理阶段。释放内存。

按照上一篇文章中对mp3文件格式的解析,可以知道sine-8k-10s.mp3文件中包含141帧,第1帧是Info,所以数据帧有140帧。数据帧的帧头为ffe3 18c4,可知,每个数据帧包含576个采样,每帧72字节(8kbps/8kHz*576/8=72)。

app_tms_mp3输出的日志:

读取编码包 #1 size= 72 字节
读取编码包 #1 前8个字节 ff e3 18 c4 00 0d 20 96
读取编码包 #2 size= 72 字节
读取编码包 #2 前8个字节 ff e3 18 c4 06 0e 70 f2
读取编码包 #3 size= 72 字节
读取编码包 #3 前8个字节 ff e3 18 c4 07 0d e8 a6

从音频包 #2 中读取音频帧 #1, format = fltp , sample_rate = 8000 , channels = 1 , nb_samples = 47, pts = 1016064, best_effort_timestamp = 1016064

从音频包 #3 中读取音频帧 #2, format = fltp , sample_rate = 8000 , channels = 1 , nb_samples = 576, pts = 2032128, best_effort_timestamp = 2032128
结束播放文件 /var/lib/asterisk/media/sine-8k-10s.mp3,共读取 141 个包,共 10152 字节,共生成 140 个包,共 80000 字节,共发送RTP包 140 个,采样 80000 个,耗时 10207260

sine-8k-10s.mp3

通过对比程序输出的日志和样本文件中的数据,可以看到ffmpeg通过av_read_frame读取的包,就是mp3文件中的帧,是文件中原始的未解压数据。mp3文件中每一帧的大小虽然一样(72字节),但是包含的采样数并不相同(第1包有27个,后续的包都是576个)。通过编码后,生成80000个采样,大小是80000个字节,和预期一致。

遗留问题

在asterisk中如何知道对端支持的编码格式?如果在ast_frame中指定了和用户端不一致的编码格式,asterisk会进行转码?

解码mp3文件时,AVFrame中包含pts,如何理解和使用这个数据?可以替代通过采样数计算发送间隔吗?

参考

Asterisk PBX Docker image

Using Menuselect to Select Asterisk Options

Logging Configuration

Logging

Running Asterisk

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 159,117评论 4 362
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 67,328评论 1 293
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 108,839评论 0 243
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 44,007评论 0 206
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,384评论 3 287
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,629评论 1 219
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,880评论 2 313
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,593评论 0 198
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,313评论 1 243
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,575评论 2 246
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 32,066评论 1 260
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,392评论 2 253
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 33,052评论 3 236
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,082评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,844评论 0 195
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,662评论 2 274
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,575评论 2 270