订制呼叫和收媒体处理流程的siprtp.c

这个程序创建SIP INVITE会话并处理RTP包,不同于普通pj媒体应用程序,它绕过流框架,使用自己的线程处理。用户可以设置呼叫数量、间隔、持续时间等参数,程序还包括媒体质量计算、日志记录等功能。

  与普通的pj媒体应用程序不同,此程序将绕过pj媒体的流框架,利用自己的线程手工处理RTP包。活动图如下:


 

 

//用法

static constchar*USAGE =

" 作用:                                 \n"

"   此程序创建SIP INVITE会话和媒体,并计算媒体质量(丢包、jitterrtt等) \n"

"   与普通的pj媒体应用程序不同,此程序将绕过pj媒体的流框架                \n"

"   利用自己的线程手工处理RTP                                          \n"

"\n"

" 用法:\n"

"  siprtp [options]        => 以服务模式启动\n"

"  siprtp [options] URL    => 以客户模式启动\n"

"\n"

" 程序选项:\n"

"  --count=N,        -c    设置要创建呼叫数目  (缺省:1) \n"

"  --gap=N           -g    设置呼叫间隙到N毫秒 (缺省:0)\n"

"  --duration=SEC,   -d    设置最大的呼叫持续时间 (缺省:不限制) \n"

"  --auto-quit,      -q    当呼叫完成时是否退出(缺省:)\n"

"  --call-report     -R    显示呼叫终止报告否 (缺省:)\n"

"\n"

" 地址和端口选项\n"

"  --local-port=PORT,-p    设置本地SIP端口(缺省: 5060)\n"

"  --rtp-port=PORT,  -r    设置RTP的开始端口 (缺省: 4000)\n"

"  --ip-addr=IP,     -i    Set local IP address to use (otherwise itwill\n"

"                           try to determinelocal IP address from hostname)\n"

"\n"

" 日志选项:\n"

"  --log-level=N,    -l    设置日志冗长级别 (缺省=5)\n"

"  --app-log-level=N       设置应用日志冗长级别 (缺省=3)\n"

"  --log-file=FILE         写日志到文件 FILE\n"

"  --report-file=FILE      写报告到文件 FILE\n"

"\n"

/* Don't support this anymore, because codec isproperly examined in

  pjmedia_session_info_from_sdp() function.

 

" Codec Options:\n"

"  --a-pt=PT               Set audiopayload type to PT (default=0)\n"

"  --a-name=NAME           Set audiocodec name to NAME (default=pcmu)\n"

"  --a-clock=RATE          Set audiocodec rate to RATE Hz (default=8000Hz)\n"

"  --a-bitrate=BPS         Set audiocodec bitrate to BPS (default=64000bps)\n"

"  --a-ptime=MS            Set audioframe time to MS msec (default=20ms)\n"

*/

;

 

 

//包括的头文件

#include <pjsip.h>

#include <pjmedia.h>

#include <pjmedia-codec.h>

#include <pjsip_ua.h>

#include <pjsip_simple.h>

#include <pjlib-util.h>

#include <pjlib.h>

 

#include <stdlib.h>

 

//如果禁用多线程,请解开以下注释

//注意:如果禁用多线程,则siprtp将不能传输RTP

/*

#undef PJ_HAS_THREADS

#define PJ_HAS_THREADS 0

*/

 

 

#if PJ_HAS_HIGH_RES_TIMER==0

#   error"High resolution timer is needed for this sample"

#endif

 

#define THIS_FILE   "siprtp.c"

#define MAX_CALLS   1024

#define RTP_START_PORT  4000

 

 

// 编解码器描述:

struct codec

{

    unsigned pt;

    char*    name;

    unsigned clock_rate;

    unsigned bit_rate;

    unsigned ptime;

    char*    description;

};

 

 

//当呼叫可用时,创建双向媒体流

struct media_stream

{

    //静态

    unsigned     call_index;             //呼叫所有者

    unsigned     media_index;            //呼叫中的媒体索引

   pjmedia_transport   *transport;      //用于发送/接收RTP/RTCP

 

    //是否活动

    pj_bool_t        active;                 //如果在呼叫中,则为非零值

 

    //当前流信息

   pjmedia_stream_info  si;           //当前流信息

 

    //更多信息

    unsigned     clock_rate;             //时钟速率

    unsigned     samples_per_frame; //每帧采样

    unsigned     bytes_per_frame;   //帧大小

 

    //RTP会话

    pjmedia_rtp_session  out_sess;  //呼出 RTP 会话

   pjmedia_rtp_session  in_sess;   //呼入 RTP 会话

 

    //RTCP状态

   pjmedia_rtcp_session rtcp;           //呼入 RTCP会话

 

    //线程

    pj_bool_t        thread_quit_flag;  //停止媒体线程否

   pj_thread_t      *thread;             //媒体线程

};

 

 

//当应用程序启动时,下面的呼叫结构被创建。当应用程序退出时,结构被销毁

struct call

{

    unsigned            index;

   pjsip_inv_session        *inv;

    unsigned            media_count;

    structmedia_stream  media[1];

   pj_time_val         start_time;

   pj_time_val         response_time;

   pj_time_val         connect_time;

 

   pj_timer_entry      d_timer;        //断开定时

};

 

 

//应用程序用到的全局变量

static structapp

{

    unsigned         max_calls;

    unsigned        call_gap;

    pj_bool_t           call_report;

    unsigned        uac_calls;

    unsigned        duration;

    pj_bool_t           auto_quit;

    unsigned        thread_count;

    int             sip_port;

    int             rtp_start_port;

    pj_str_t        local_addr;

    pj_str_t        local_uri;

    pj_str_t        local_contact;

   

    int             app_log_level;

    int             log_level;

    char             *log_filename;

    char             *report_filename;

 

    structcodec    audio_codec;

 

    pj_str_t        uri_to_call;

 

   pj_caching_pool  cp;

    pj_pool_t            *pool;

 

   pjsip_endpoint   *sip_endpt;

    pj_bool_t           thread_quit;

   pj_thread_t      *sip_thread[1];

 

   pjmedia_endpt        *med_endpt;

    structcall      call[MAX_CALLS];

} app;

 

 

 

//原型声明:

 

//当呼叫中的SDP协商完成时,函数被回调

static void call_on_media_update( pjsip_inv_session *inv,

                  pj_status_t status);

 

//INVITE会话状态变化时,函数被回调

static void call_on_state_changed( pjsip_inv_session *inv,

                   pjsip_event *e);

 

//当对话被复制后,函数被回调

static void call_on_forked(pjsip_inv_session *inv, pjsip_event *e);

 

//当在对话外收到呼入请求时,函数被回调

//* Callback to be called to handle incomingrequests outside dialogs: */

static pj_bool_t  on_rx_request(pjsip_rx_data *rdata );

 

//工作线程原型

static int sip_worker_thread(void *arg);

 

//创建用于呼叫的SDP

static pj_status_t  create_sdp(pj_pool_t *pool,

                   structcall *call,

                   pjmedia_sdp_session **p_sdp);

 

//挂断呼叫

static void hangup_call(unsigned index);

 

//销毁呼叫媒体

static void destroy_call_media(unsigned call_index);

 

//销毁媒体

static void destroy_media();

 

//当收到RTP包时,此函数被回调

static void on_rx_rtp(void *user_data, void*pkt, pj_ssize_t size);

 

//当收到RTCP包时,此函数被回调

static void on_rx_rtcp(void *user_data, void*pkt, pj_ssize_t size);

 

//显示错误

static void app_perror(const char*sender, const char*title,

               pj_status_t status);

 

//打印呼叫信息

static void print_call(int call_index);

 

 

//应用程序使用下面的PJSIP注册模块,控制对话或事务外的呼入请求,

//此处的主要目的是为了控制呼入INVITE请求消息。

static pjsip_module  mod_siprtp =

{

    NULL, NULL,             /* prev, next.       */

    { "mod-siprtpapp", 13 },     /* Name.         */

    -1,                 /* Id            */

   PJSIP_MOD_PRIORITY_APPLICATION, /*Priority          */

    NULL,                /* load()            */

    NULL,                /* start()           */

    NULL,                /* stop()            */

    NULL,                /* unload()          */

    &on_rx_request,      /*on_rx_request()      */

    NULL,                /* on_rx_response()      */

    NULL,                /* on_tx_request.        */

    NULL,                /* on_tx_response()      */

    NULL,                /* on_tsx_state()        */

};

 

 

//编解码器列表

struct codec audio_codecs[] =

{

    { 0,  "PCMU",8000, 64000, 20, "G.711 ULaw" },

    { 3,  "GSM",  8000, 13200, 20, "GSM"},

    { 4,  "G723",8000, 6400,  30, "G.723.1"},

    { 8,  "PCMA",8000, 64000, 20, "G.711 ALaw" },

    { 18, "G729", 8000, 8000, 20, "G.729" },

};

 

 

//初始化SIP协议栈

static pj_status_t  init_sip()

{

    unsignedi;

   pj_status_t status;

 

    //初始化PJLIB-UTIL:

    status =pjlib_util_init();

   PJ_ASSERT_RETURN(status == PJ_SUCCESS, status);

 

    //在分配内存之前,必须创建pool factory!

   pj_caching_pool_init(&app.cp, &pj_pool_factory_default_policy,0);

 

    //创建应用程序内存池

    app.pool= pj_pool_create(&app.cp.factory, "app",1000, 1000, NULL);

 

    //创建SIP终端

    status =pjsip_endpt_create(&app.cp.factory, pj_gethostname()->ptr,

                &app.sip_endpt);

   PJ_ASSERT_RETURN(status == PJ_SUCCESS, status);

 

 

    //添加UDP传输端口

    {

    pj_sockaddr_in addr;

    pjsip_host_port addrname;

    pjsip_transport *tp;

 

    pj_bzero(&addr,sizeof(addr));

    addr.sin_family= pj_AF_INET();

    addr.sin_addr.s_addr= 0;

    addr.sin_port= pj_htons((pj_uint16_t)app.sip_port);

 

    if(app.local_addr.slen) {

 

        addrname.host = app.local_addr;

        addrname.port = app.sip_port;

 

        status = pj_sockaddr_in_init(&addr,&app.local_addr,

                     (pj_uint16_t)app.sip_port);

        if (status!= PJ_SUCCESS) {

        app_perror(THIS_FILE, "Unable to resolve IPinterface", status);

        returnstatus;

        }

    }

 

    //启动UDP侦听

    status =pjsip_udp_transport_start( app.sip_endpt, &addr,

                        (app.local_addr.slen ? &addrname:NULL),

                        1, &tp);

    if(status != PJ_SUCCESS) {

        app_perror(THIS_FILE,"Unable to start UDP transport",status);

        return status;

    }

 

    PJ_LOG(3,(THIS_FILE, "SIP UDP listening on%.*s:%d",

          (int)tp->local_name.host.slen,tp->local_name.host.ptr,

          tp->local_name.port));

    }

 

//初始化事务层

//将创建/初始化事务hash表。。。

    status =pjsip_tsx_layer_init_module(app.sip_endpt);

   PJ_ASSERT_RETURN(status == PJ_SUCCESS, status);

 

    //初始化UA

    status =pjsip_ua_init_module( app.sip_endpt, NULL);

   PJ_ASSERT_RETURN(status == PJ_SUCCESS, status);

 

    //初始化100rel支持

    status =pjsip_100rel_init_module(app.sip_endpt);

   PJ_ASSERT_RETURN(status == PJ_SUCCESS, status);

 

    //初始化INVITE会话模块

    {

    pjsip_inv_callback inv_cb;

 

    //初始化INVITE会话回调

    pj_bzero(&inv_cb,sizeof(inv_cb));

    inv_cb.on_state_changed= &call_on_state_changed;

    inv_cb.on_new_session= &call_on_forked;

    inv_cb.on_media_update= &call_on_media_update;

 

    //初始化INVITE会话模块

    status =pjsip_inv_usage_init(app.sip_endpt, &inv_cb);

    PJ_ASSERT_RETURN(status== PJ_SUCCESS, 1);

    }

 

    //注册模块用于收到呼入请求处理

    status =pjsip_endpt_register_module( app.sip_endpt, &mod_siprtp);

   PJ_ASSERT_RETURN(status == PJ_SUCCESS, status);

 

    //初始化全部呼叫

 

评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符  | 博主筛选后可见
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值