编程实践通信与流媒体

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

2020-08-13  本文已影响0人  全栈顾问

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

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 -rx "core restart now"

asterisk -rx "logger rotate"

asterisk -rx "pjsip set logger on"

开发应用

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);
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

上一篇 下一篇

猜你喜欢

热点阅读