首先配置linux的开发环境及asterisk的源码.linux
建立的文件名为app_helloworld.c,存放在Asterisk的源代码树/apps目录下app
代码以下:函数
#include "asterisk.h" //每一个Asterisk模块都包含主要的Asterisk头文件 #include "asterisk/module.h" //包含ASTERISK_FILE_VERSION宏,该宏用于注册该文件的版本#include "asterisk/logger.h" ASTERISK_FILE_VERSION(__FILE__, "$Revision: 100001 $")//该宏用于注册该文件的版本 static int load_module(void) //Asterisk加载和卸载模块时会调用他们 { ast_log(LOG_NOTICE, "Hello World!\n"); return AST_MODULE_LOAD_SUCCESS; } static int unload_module(void) { ast_log(LOG_NOTICE, "Goodbye World!\n"); return 0; } AST_MODULE_INFO_STANDARD(ASTERISK_GPL_KEY, "Hello World");
编绎成功后,将app_helloworld.so 拷贝到 /usr/lib/asterisk/modules目录下ui
CLI>module load app_helloworld.sospa
CLI>Loaded app_helloworld.so => (Hello World)线程
装载成功,这只是个空的模块,下面咱们添加些应用。 code
asterisk里提供了大量的app用于在extension.conf中编写dialplan,这些app很是丰富,你几乎能够作任何事情,你甚至能够在dialplan中设置变量,使用一些简单的流程控制等。可是当你发现你须要的app在asterisk里并无提供,而老板又每天催你交差的时候,你就不得不本身来实现它,建议你们把本身实现的app放在一个新的模块里,好比咱们能够创建一个新的名为app_myapps.so的模块,那么这就又牵涉到如何添加一个新的模块。 ip
首先,除了一些内建的app之外,全部的app都是在模块中定义的,当这个模块载入后,模块中定义的app会注册到asterisk中,这些app被按照字母序放在一个链表中,asterisk中习惯用如下宏初始化一个全局链表: 开发
#define AST_LIST_HEAD_STATIC(name, type) struct name { struct type *first; struct type *last; ast_mutex_t lock; } name;
好比初始化apps链表:static AST_LIST_HEAD_STATIC(apps, ast_app); rem
那么内建的app在哪里定义的?main/pbx.c
在这个文件中定义了不少内建命令,好比answer, hangup, busy, backgroud等,在load_pbx()函数中被注册,正是这些在pbx中定义的built-in使得asterisk能够同时使用各类协议(sip, h323, iax2, zaptel, gtalk等),成为一个相似协议转换器的东西,并且也有公司确实就拿blackfin下移植的asterisk作了pstn网关产品,asterisk 具备分层的结构,在pbx层处理一些通用的共性的操做,而后根据channel的不一样,调用相关的回调函数,来实现对不一样协议的处理。如今以answer 为例来讲明一下:
假设context以下:
exten => s, 1, Answer()
exten => s, 2, Echo()
当有呼叫进入时,在do_monitor线程中sipsock_read得到Invite消息,而后handle_request函数调用 handle_request_invite函数处理这个Invite消息,若是这是一个新的dialog,那么调用sip_new函数会返回一个 ast_channel结构体,而后调用ast_pbx_start,在 pbx_thread线程中调用__ast_pbx_run,而后调用ast_spawn_extension,再调用 pbx_extension_help,在这个函数中,会调用pbx_findapp()来根据extension中的app名查找对应的处理函数,在这个例子中,会找到pbx_builtin_answer,这个函数的第一个参数是一个channel结构体,这里此ast_channel是一个sip channel,那么它调用的answer函数其实就是chan_sip.so中定义的sip_answer()函数,如今你们明白了吧,对于pbx层来讲,他根本就不须要知道是哪一个channel,这就是分层的好处。
好了,如今言归正传,一个模块文件要放在apps目录下,并用app_做为文件名的前缀,这里咱们在apps目录下创建一个名为app_myapps.c 的新文件。其实添加一个新的模块和app很是的简单,那么一个模块须要哪些必备的要素呢?通常来讲load_module()和 unload_module()这两个函数都是必须的,load_module()在载入这个模块时会被调用,因此咱们要在这个函数里对app进行注册;在unload_module()时咱们须要注销这个app并强制挂断全部正在使用这个模块的用户。那么下面就给出一个建立模块和新的app的代码模版吧:
#include "asterisk.h" ASTERISK_FILE_VERSION(__FILE__, "$Revision: 1.1 $") //这里是asterisk core用来控制源码文件的版本的 #include <stdlib.h> #include <unistd.h> #include <string.h> #include <stdlib.h> #include <ctype.h> #include <stdio.h> #include <asterisk/lock.h> #include <asterisk/file.h> #include <asterisk/logger.h> #include <asterisk/channel.h> #include <asterisk/pbx.h> #include <asterisk/options.h> #include <asterisk/config.h> #include <asterisk/module.h> #include <asterisk/enum.h> #include <asterisk/utils.h> #include <asterisk/app.h> //to add any include files you need here static char *tdesc = "This is my app :)"; static char *app = "myapp"; static char *synopsis = "myapp(param1|param2):\n" "This Application will do something, urr?\n"; static int myapp_exec(struct ast_channel *chan, void *data) { int res=0; char *parse; AST_DECLARE_APP_ARGS(args, AST_APP_ARG(param1); AST_APP_ARG(param2); ); struct ast_module_user *u; if (ast_strlen_zero(data)) { ast_log(LOG_WARNING, "MYAPP requires an argument (param1|param2)\n"); return -1; } u = ast_module_user_add(chan); parse = ast_strdupa(data); AST_STANDARD_APP_ARGS(args, parse); if (ast_strlen_zero(args.param1) || ast_strlen_zero(args.param2)) { ast_log(LOG_WARNING, "MYAPP requires an argument (callee|filename)\n"); res = -1; goto out1; } /* * to do anything you want to here, params are args.param1 and args.param2. */ out1: ast_module_user_remove(u); return res; } static int load_module(void) { return ast_register_application(app, myapp_exec, synopsis, tdesc); } static int unload_module(void) { int res; res = ast_unregister_application(app); ast_module_user_hangup_all(); return res; } AST_MODULE_INFO_STANDARD(ASTERISK_GPL_KEY, "MY OWN APPS");
好了,一个名叫myapp的extension应用就写好了,你能够给他传两个参数,参数之间用|隔开,若是你没有修改modules.conf的话,编译后从新启动一次asterisk会自动加载app_myapps.so这个模块的.