Asterisk媒体处理分析1 媒体转换过程以下讲媒体转换有两种方式除外:⏹两端编码方式一致如两端语音编码都是g729;⏹一端是g711a,另一端是g711u时Asterisk媒体处理过程与业务无关,是一个统一的过程,处理对象有RTP和File,不管外界RTP或File输入格式如何,进入Asterisk后默认转换为signed linear格式,从Asterisk 输出(发送或写文件)时再从slinear格式转换为外界RTP或File格式(细节参见RTP Translate):上图分为四个方向1. RTP to RTP(正常通话流程)2. RTP to File(录音流程)3. File to RTP(放音流程)4. File to File(文件格式转换流程)复杂业务的媒体处理可以分解为以上四个方向处理流程,复杂业务可能包含其中几种流程,每个业务的媒体处理可以用下图表示:1设置channel媒体转换路径(如g723=>gsm)2 源媒体转换为slinear格式(如g723=>slinear)4 Slinear转换为目的格式(如slinear=>gsm)3 媒体处理(如混音)5 发送/写文件1. 设置转换路径,即设置源媒体格式与目的媒体格式;2. 把输入媒体格式进行转换为slinear,如果输入媒体为slinear可省略本步骤;3. 媒体处理,如混音等,一般可以省略;4. 把slinear格式媒体转换为目的媒体格式,如果目的媒体格式为slinear可省略本步骤;5. 把转换后的媒体打包发送或写到本地文件。
1.1 RTP to RTPRTP to RTP适用于正常通话流程。
Channel收到RTP包时经过分析可以得到媒体格式,每个channel保存读和写的媒体格式信息,读RTP时使用步骤1和2,发送RTP时使用步骤4和5,channel的结构参见媒体转换相关结构,channel里保存媒体转换信息结构ast_trans_pvt和文件流描述结构ast_filestream(见文件流)。
下图为一个正常通话的编码转换过程,通话一端适用ilbc编码另一端使用gsm编码:从上图看出,通话两端的语音编码进入asterisk后都转换为asterisk内部编码slinear后,发送时再被转换为目的语音编码。
下图说明一端使用g711a,另一端使用g711u时不需要转换为asterisk内部编码slinear,而是alaw和ulaw两种编码直接进行转换:1.2 RTP to FileRTP to File适用录音流程,包含步骤1、2、4、5。
例:g729.cap语音还原为gsm格式文件过程:File to RTP适用于放音流程,与RTP to File流程一样也包含步骤1、2、4、5。
1.4 File to FileFile to File适用于文件格式转换,与RTP to File/File to RTP一样也包含步骤1、2、4、5。
具体实例可参见附录。
1.5 混合流程混合流程用到以上四个流程中的某几个复杂的业务有可能是混合流程,如Voicemail包含File to RTP和RTP to File流程,具体参见voicemail函数调用过程。
2 RTP处理2.1 RTP init在创建sip_channel时,rtp fd同时被创建并加进channel的fds,通过poll进行监控。
sip_new -> tmp->fds[0] = ast_rtp_fd(i->rtp);2.2 RTP ReadPoll监控到rtp fd有读事件时进行read;ast_rtp_read中进行RTP解包;读RTP过程中会调用ast_translate函数进行媒体转换,一般转换为slinear格式。
具体函数调用过程如下:wait_for_answer -> ast_waitfor_n(watchers, pos, to); -> ast_waitfor_nandfds->poll-> f = ast_read(winner) -> struct ast_frame *ast_read(struct ast_channel *chan) -> static struct ast_frame *__ast_read(struct ast_channel *chan, int dropaudio) ->f = chan->tech->read(chan)即static struct ast_frame *sip_read(struct ast_channel*ast) / ast_translate ->static struct ast_frame*sip_rtp_read(struct ast_channel *ast, struct sip_pvt *p, int *faxdetect) -> ast_set_read_format/ast_set_write_format/f =ast_rtp_read(p->rtp) RTP解包-> recvfrom发送RTP时会调用ast_translate函数进行媒体转换,从slinear格式转换为目的媒体格式,ast_rtp_raw_write函数中对RTP进行组包。
具体函数调用流程如下:wait_for_answer-> f = ast_read(winner) ->int ast_write(struct ast_channel *chan, struct ast_frame *fr) ->ast_set_write_format/ast_translate/res = chan->tech->write(chan, f) 即static intsip_write(struct ast_channel *ast, struct ast_frame *frame) ->int ast_rtp_write(struct ast_rtp *rtp, struct ast_frame *_f) ->ast_rtp_raw_write(rtp, f, codec) RTP组包-> sendto2.4 RTP translate2.4.1 媒体转换相关结构ast_channel结构中writetrans和readtrans指向ast_trans_pvt结构,writetrans和readtrans 通过ast_translator_build_path创建;ast_translator 结构中保存媒体转换源编码格式(srcfmt),目的编码格式(dstfmt),转换回调(framein frameout):以g729编码为例初时化ast_translator结构:2.4.2 媒体转换流程2.4.2.1 注册编码把某种编码ast_translator注册到tr_matrix全局变量:ast_register_translator(t) (如res = ast_register_translator(&g729tolin)) ->int __ast_register_translator(struct ast_translator *t, struct ast_module *module) -> static void rebuild_matrix(int samples)2.4.2.2 设置编码转换路径static struct ast_frame *sip_rtp_read(struct ast_channel *ast, struct sip_pvt *p, int *faxdetect) -> int ast_set_read_format(struct ast_channel *chan, int fmt) / int ast_set_write_format(struct ast_channel *chan, int fmt) ->static int set_format(struct ast_channel *chan, int fmt, int *rawformat, int *format,struct ast_trans_pvt **trans, const int direction) ->struct ast_trans_pvt *ast_translator_build_path(int dest, int source)调用ast_translator_build_path函数,在全局变量tr_matrix中查找对应的ast_translator 创建ast_trans_pvt并返回:2.4.2.3 编码转换调用ast_translate函数实现编码转换:3 T38Asterisk支持pass-through方式T38传真,asterisk不发起reinvite,只是代理T38。
T38 reinvite经过asterisk时SDP协商的纠错方式,频率等信息可能被修改,T38包经过asterisk 时UDPTLPacket被解析,具体T30内容不会被修改。
4 附录4.1 文件流API4.1.1 文件流操作函数●struct ast_filestream *ast_readfile(const char *filename, const char *type, const char*comment, int flags, int check, mode_t mode)打开一个可读文件流,类似以只读方式fopen。
●struct ast_filestream *ast_writefile(const char *filename, const char *type, const char*comment, int flags, int check, mode_t mode)打开一个可写文件流,类似以写方式fopen。
●struct ast_frame *ast_readframe(struct ast_filestream *s)读取文件流数据,类似fread。
●int ast_writestream(struct ast_filestream *fs, struct ast_frame *f)往文件流中写数据,类似fwrite如果读和写的格式不一致会调用ast_translate进行转换。
●int ast_closestream(struct ast_filestream *f)关闭文件流,类似fclose。