PJSIP学习笔记15 -- PJSUA应用程序中的会议桥
注: 上面的tpport,是我为了帮助理解对应关系,为app_config增加的成员变量, 源码中并无这个成员。
可以使用PJSUA程序控制台命令cl来查看系统中的conference_port
未拨打电话时,如下:
Port #00[24KHz/20ms/1] Master/sound transmitting to:
Port #01[24KHz/20ms/1] ringback transmitting to:
Port #02[24KHz/20ms/1] ring transmitting to:
拨打电话,并成功接通后如下:
Port #00[24KHz/20ms/1] default transmitting to: #3
Port #01[24KHz/20ms/1] ringback transmitting to:
Port #02[24KHz/20ms/1] ring transmitting to:
Port #03[ 8KHz/20ms/1] sip:111@192.168.200.11:5062 transmitting to: #0
可见:在默认配置条件下,pjsua程序启动后,只存在3个有效的会议桥端口, 通过跟踪调试app_init()函数也可以证实这一点;我们的个性化的铃声音效,可以通过添加wave文件的player连接到player.slot来实现。
pjsua程序提供了会议桥端口的connect/disconnect命令, 可以实现会议桥的个端口的媒体数据的流动方向,我们可以使用这两个命令来加深对会议桥工作方式的理解。
顺便写一句备忘: 关于彩铃,也就是早期媒体,可以看看这篇文章; 另外关于PRACK,相关的编准RFC5626,在SIP账户的配置参数中 use_rfc5626,与这个有关系,180响应携带SDP的原因应该与早期媒体由关系。
pjsua还为我们分别演示了,在通话建立后,通过DTMF音频或INFO消息向对方发送按键音效的方法, 具体分别参考ui_send_dtmf_2833()函数和ui_send_dtmf_info()函数
pjsua_call_send_request(current_call, &SIP_INFO, &msg_data_);被用来发送一个dialog内的SIP INFO信息
pjsua_call_dial_dtmf(current_call, &digits); 被用来发送一个dialog内的DTMF/RTP包
===== 下面重点分析一下DTMF音频的收发
RF2833规定的DTMF字符表:
/* RFC 2833 digit */
static const char digitmap[17] = { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '*', '#', 'A', 'B', 'C', 'D', 'R'};
使用DTMF事件event->event做索引, 可以查询对端发送的数字是哪一个。
1)DTMF接收端的handle_incoming_dtmf():
在handle_incoming_dtmf代码中: pjmedia_rtp_dtmf_event *event = (pjmedia_rtp_dtmf_event*) payload;
说明DTMF/RRT负载包净负载是一个结构化的数据,共4个字节,负载类型101(---通过抓包工具验证)
解析出的DTMF数据,如果注册了回调函数, 则使用回调函数处理接收的事件, 否则数据包保存在stream->rx_dtmf_buf[]缓冲区里面
*** pjsua程序中,该回调函数在pjsua_aud_channel_update()函数中被设置, 指向函数dtmf_callback()
该函数使用应用层pjsua_var.ua_cfg.cb.on_dtmf_digit回调函数继续向应用层回调,我们来看看应用层回调的设置:
app_config.cfg.cb.on_dtmf_digit = &call_on_dtmf_callback; 这个回调函数只是提供了一个打印log的操作。
***为什么dtmf_event的RTP负载类型是101? 在SDP解析函数get_audio_codec_info_param()中,由这么一句:
si->rx_event_pt = pj_strtoul(&r.pt);
2)DTMF发送端
pjsua_call_dial_dtmf(current_call, &digits);被用来发送一个dialog内的DTMF音频媒体
我们来看看pjsua_call_dial_dtmf的内部实现的关键调用:
pjmedia_stream_dial_dtmf(call->media[call->audio_idx].strm.a.stream, digits);
对于每个pj_mediastream存在一个DTMF发送缓冲区,pjmedia_stream_dial_dtmf用来填充这个缓冲区:
stream->tx_dtmf_buf[stream->tx_dtmf_count+i].event = pt;
stream->tx_dtmf_buf[stream->tx_dtmf_count+i].duration = 0;
stream->tx_dtmf_buf[stream->tx_dtmf_count+i].ebit_cnt = 0;
这个缓冲区的数据将通过如下的途径被使用:
put_frame()->put_frame_imp()->create_dtmf_payload()->pjmedia_rtp_encode_rtp()
对于DTMF:
create_dtmf_payload函数每次构建一个4字节的event/RTP数据包的payload数据
tx_dtmf_buf中的每个event会被重复发送多次(12次),
在12次重复发送过程中, RTP时间标签不会发生变化,但RTP序列号或连续递增
并且,每次发送的event数据中duration会增加固定增量,该增量使用宏PJMEDIA_PIA_SPF计算, 计算原理暂时还没高明白
当发送下一个DTMF字符时, 下一个DTMF字符时间标签的增量等于,上一个DTMF字符的最后一个event数据序列中的duration -- 尚未仔细验证,大方向是这样的
负载长度永远为4(负载是一个结构体, 结构体长度必须是4, 程序中存在如下代码:pj_assert(sizeof(pjmedia_rtp_dtmf_event) == 4))
负载的赋值如下:
event->event = (pj_uint8_t)digit->event; //按键字符表digitmap[]数组索引
event->e_vol = 10;
event->duration = pj_htons((pj_uint16_t)digit->duration);//这个值像时间标签一样是单向递增的
3)关于DTMF的频率成分
=== 关于电话音效, 主要在on_call_state()回调中
回铃音: ringback_start() ->pjsua_conf_connect(app_config.ringback_slot, 0); //0是master_port所在端口
ring_stop () ->pjsua_conf_disconnect(app_config.ringback_slot, 0); //0是master_port所在端口 -----注意是ring_stop,不是ringback_stop
震铃音: ring_start() ->pjsua_conf_connect(app_config.ring_slot, 0); //0是master_port所在端口
ring_stop () ->pjsua_conf_disconnect(app_config.ring_slot, 0); //0是master_port所在端口
忙音(busy 486): 未实现
传统话机的铃音标准GB3380:http://ishare.iask.sina.com.cn/f/13367325.html
本机震铃音,给个下载网址:http://sc.chinaz.com/tag_yinxiao/DianHuaLingSheng.html
PJSIP学习笔记15 -- PJSUA应用程序中的会议桥相关推荐
- Hadoop学习笔记—15.HBase框架学习(基础知识篇)
Hadoop学习笔记-15.HBase框架学习(基础知识篇) HBase是Apache Hadoop的数据库,能够对大型数据提供随机.实时的读写访问.HBase的目标是存储并处理大型的数据.HBase ...
- C++语言学习笔记15:Clean 垃圾清理插件
C++语言学习笔记15:Clean 垃圾清理插件 对话框 STET1 图片切换功能 导入位图资源 插入图片控件并修改属性 添加消息处理函数 step2 开发思路及类关系图 step3 添加控件及MFC ...
- Linux进程线程学习笔记:运行新程序
Linux进程线程学习笔记:运行新程序 周银辉 在上一篇中我们说到,当启动一个新进程以后,新进程会复制父进程的大部份上下文并接着运行父进程中的代码,如果我们使新进程不运行原父进程的代码,转而运行另外一 ...
- Sharepoint学习笔记---如何在Sharepoint2010网站中整合Crystal Report水晶报表(显示数据 二)...
在Sharepoint学习笔记---如何在Sharepoint2010网站中整合Crystal Report水晶报表(显示数据一)中,解释了如何把Crystal Report整合到Sharepoint ...
- 区块链学习笔记15——ETH状态树
区块链学习笔记15--ETH状态树 学习视频:北京大学肖臻老师<区块链技术与应用> 笔记参考:北京大学肖臻老师<区块链技术与应用>公开课系列笔记--目录导航页 引入 要实现的功 ...
- oracle复制另一个字段,【学习笔记】Oracle存储过程 表中列不同时动态复制表中数据到另一个表中...
天萃荷净 分享一篇关于Oracle存储过程实现表之间数据复制功能.两表中列不同,动态的将一表中的数据复制到另一个表中案例 因为要用到回收站功能,删除一条记录,要先放到一个delete表中,以便以后恢复 ...
- 数据结构与算法学习笔记15:最大流问题 / 二分图 / 有权无权二分图的匹配 / 匈牙利算法 / 银行家算法 / 稳定婚配
数据结构与算法学习笔记15:最大流问题 / 二分图 / 有权无权二分图的匹配 / 匈牙利算法 / 银行家算法 / 稳定婚配 引入小题:最短路径 最大流问题(maximum flow problem) ...
- 点云学习笔记15——PCL常用的基础代码
点云学习笔记15--PCL基础 命名规范 常用代码 1.时间计算 2.pcl::PointCloud::Ptr和pcl::PointCloud的两个类相互转换 3.如何查找点云的x,y,z的极值? 4 ...
- 10-1Python学习笔记 10-2C语言学习笔记 : 在文本编辑器中新建一个文件, 写几句话来总结一下你至此学到的Python知识
10-1 Python学习笔记 : 在文本编辑器中新建一个文件, 写几句话来总结一下你至此学到的Python知识, 其中每一行都以"In Python you can"打头. 将这 ...
最新文章
- 我们是如何解决复杂系统扩展性问题的
- SDUT 1265-马停下过河卒(DFS)
- CG CTF WEB 签到题
- 学会Python,我们可以从事哪几类工作呢?
- ML.NET Cookbook:(10)如何使用模型做出一个预测?
- centos桥接模式怎么联网_今日回收 | 互联网+废品回收模式是怎么兴起的呢?
- 彻夜怒肝!SpringBoot+Sentinel+Nacos高并发已撸完
- 25k英里高速建48个充电走廊,美国电动汽车产业迎来春天
- King's Quest - poj 1904(强连通分量+外挂输入输出)
- 【每日一题(26)】初等排序算法(3) 插入排序 希尔排序 (更正)
- 业务中台--如何设计企业级权限管理系统
- 详解三相直流无刷电机驱动器硬件原理图
- 基于MODIS数据的NDVI与LST相关性分析(IDL代码实现)
- iOS中Instrument的使用
- 喹啉羧酸类 DHODH 抑制剂用于治疗急性髓系白血病
- 使用ffmpeg在视频中心添加透明水印
- pr2020lut导入_pr lut预设怎么安装-PR下导入lut预设的方法 - 河东软件园
- linux打包文件夹命令
- SysML图例-核聚变
- CSS空格和换行的处理
热门文章
- 三、Python学习(三)海龟模块turtle使用案列-奥迪图标
- 如何在合并多段视频时,共用同一个片头片尾
- SDL介绍----2、SDL安全设计核心原则
- node 生产的env文件怎么注入_前端各种文件上传攻略,从小图片到大文件断点续传...
- android动画sin cos,Android开发中计算器的sin、cos及tan值计算问题分析
- 如何使用Excel的数据做条件去数据库查询数据?
- 关于RF射频走线的开窗和Soder层的避让
- 如何配置路由器接口IP,手把手教你配置DHCP
- 风变编程python小课怎么样_风变编程Python小课最近很火,大家学完感受如何?
- linux bond设备删除,删除修改bond