Simple GB28181 System
I. Deployment / Architecture Block Diagram
II. Resources Used
1. freeswitch —— sip server and mediastream transmission
2. libexosip2, libosip2, ortp, mediastream —— sip user agent
3. ffmpeg —— ps remux h264/h265
III. GB28181 SIP Signaling Channel
1. Install Freeswitch
Freeswitch Tutorial
https://www.cnblogs.com/dong1/p/10412847.html
2. Build libexosip2-5.1.0.tar.gz + libosip2-5.1.0.tar.gz
SIP UserAgent (B2BUA client)——libosip2 libeXosip2
https://www.cnblogs.com/dong1/p/10258344.html
3. sip user agent application based on libexosip2-5.1.0/tools/sip_reg.c
1)register
libexosip2-5.1.0/help/doxygen/ht2-registration.dox
/** * @ingroup libeXosip2 The eXtented eXosip stack * @defgroup howto_registration How-To send or update registrations. <H2>Initiate a registration</H2> To start a registration, you need to build a default REGISTER request by providing several mandatory headers. You can start as many registration you want even in one eXosip_t context. ~~~~~~~{.c} osip_message_t *reg = NULL; int rid; int i; eXosip_lock (ctx); rid = eXosip_register_build_initial_register (ctx, "sip:me@sip.antisip.com", "sip.antisip.com", NULL, 1800, ®); if (rid < 0) { eXosip_unlock (ctx); return -1; } osip_message_set_supported (reg, "100rel"); osip_message_set_supported (reg, "path"); i = eXosip_register_send_register (ctx, rid, reg); eXosip_unlock (ctx); return i; ~~~~~~~ The returned element of eXosip_register_build_initial_register is the registration identifier that you can use to update your registration. In future events about this registration, you'll see that registration identifier when applicable. <H2>Contact headers in REGISTER</H2> You should let eXosip2 manage contact headers alone. The setup you have provided will generate various behavior, all being controlled by eXosip2. See the "NAT and Contact header" section in setup documentation. <H2>Set password(s)!</H2> Usually, you will need a login/password to access a service! eXosip can be used to access several service at the same time and thus the realm (identify the service) needs to be provided if you are using several services in the same instance. The simple way when you use one service with username being the same as the login: ~~~~~~~{.c} eXosip_lock (ctx); eXosip_add_authentication_info (ctx, login, login, passwd, NULL, NULL); eXosip_unlock (ctx); ~~~~~~~ OR the complete way: ~~~~~~~{.c} eXosip_lock (ctx); eXosip_add_authentication_info (ctx, login, login, passwd, NULL, "sip.antisip.com"); eXosip_add_authentication_info (ctx, from_username, login, passwd, NULL, "otherservice.com"); eXosip_unlock (ctx); ~~~~~~~ <H2>Delete all registration</H2> This feature is not advised by sip specification, but it exists for some purpose & reasons! You can send "*" in Contact header of a REGISTER to ask for immediate removal of all active registrations for a particular SIP agent: ~~~~~~~{.c} rid = eXosip_register_build_initial_register (ctx, "sip:me@sip.antisip.com", "sip.antisip.com", "*", 1800, ®); ~~~~~~~ <H2>Update a registration</H2> You just need to reuse the registration identifier: ~~~~~~~{.c} int i; eXosip_lock (ctx); i = eXosip_register_build_register (ctx, rid, 1800, ®); if (i < 0) { eXosip_unlock (ctx); return -1; } eXosip_register_send_register (ctx, rid, reg); eXosip_unlock (ctx); ~~~~~~~ <b>Note</b>: The above code also shows that the stack is sometimes able to build and send a default SIP messages with only one API call <H2>Closing the registration</H2> A softphone should delete its registration on the SIP server when terminating. To do so, you have to send a REGISTER request with the expires header set to value "0". The same code as for updating a registration is used with 0 instead of 1800 for the expiration delay. ~~~~~~~{.c} int i; eXosip_lock (ctx); i = eXosip_register_build_register (ctx, rid, 0, ®); if (i < 0) { eXosip_unlock (ctx); return -1; } eXosip_register_send_register (ctx, rid, reg); eXosip_unlock (ctx); ~~~~~~~ <H2>Discard registration context</H2> If you need to delete a context without sending a REGISTER with expires 0, you can use eXosip_register_remove to release memory. ~~~~~~~{.c} int i; eXosip_lock (ctx); i = eXosip_register_remove (ctx, rid); if (i == 0) { } eXosip_unlock (ctx); ~~~~~~~ */
2) call
libexosip2-5.1.0/help/doxygen/ht1-callcontrol.dox
/** * @ingroup libeXosip2 The eXtented eXosip stack * @defgroup howto_callcontrol How-To initiate, modify or terminate calls. eXosip2 offers a flexible API to help you controling calls. <H2>Initiate a call</H2> To start an outgoing call, you typically need a few headers which will be used by eXosip2 to build a default SIP INVITE request. The code below is used to start a call: ~~~~~~~{.c} osip_message_t *invite; int cid; int i; i = eXosip_call_build_initial_invite (ctx, &invite, "<sip:to@antisip.com>", "<sip:from@antisip.com>", NULL, // optional route header "This is a call for a conversation"); if (i != 0) { return -1; } osip_message_set_supported (invite, "100rel"); { char tmp[4096]; char localip[128]; eXosip_guess_localip (ctx, AF_INET, localip, 128); snprintf (tmp, 4096, "v=0\r\n" "o=jack 0 0 IN IP4 %s\r\n" "s=conversation\r\n" "c=IN IP4 %s\r\n" "t=0 0\r\n" "m=audio %s RTP/AVP 0 8 101\r\n" "a=rtpmap:0 PCMU/8000\r\n" "a=rtpmap:8 PCMA/8000\r\n" "a=rtpmap:101 telephone-event/8000\r\n" "a=fmtp:101 0-11\r\n", localip, localip, port); osip_message_set_body (invite, tmp, strlen (tmp)); osip_message_set_content_type (invite, "application/sdp"); } eXosip_lock (ctx); cid = eXosip_call_send_initial_invite (ctx, invite); if (cid > 0) { eXosip_call_set_reference (ctx, i, reference); } eXosip_unlock (ctx); return i; ~~~~~~~ The above code is using eXosip_call_build_initial_invite to build a default SIP INVITE request for a new call. You have to insert a SDP body announcing your audio parameter for the RTP stream. The above code also show the flexibility of the eXosip2 API which allow you to insert additionnal headers such as "Supported: 100rel" (announcing support for a SIP extension). Thus you can enterely control the creation of SIP requests. The returned element of eXosip_call_send_initial_invite is the cid (call identifier) that you can use to send a CANCEL. In future events other than 100 Trying, you'll also get the did (dialog identifier) that will also be needed to control established calls. eXosip_call_set_reference is also a mean to attach one of your own context to a call so that you'll get your pointer back in eXosip_event. <H2>Answer a call</H2> The code below is another example that teach you how to answer an incoming call. You'll usually need to send a "180 Ringing" SIP answer when receiving a SIP INVITE: ~~~~~~~{.c} eXosip_lock (ctx); eXosip_call_send_answer (ctx, evt->tid, 180, NULL); eXosip_unlock (ctx); ~~~~~~~ <b>Note</b>: The above code also shows that the stack is sometimes able to build and send a default SIP messages with only one API call Then, when the user wants to answer the call, you'll need to send a 200 ok and insert a SDP body in your SIP answer: ~~~~~~~{.c} osip_message_t *answer = NULL; eXosip_lock (ctx); i = eXosip_call_build_answer (ctx, evt->tid, 200, &answer); if (i != 0) { eXosip_call_send_answer (ctx, evt->tid, 400, NULL); } else { i = sdp_complete_200ok (evt->did, answer); if (i != 0) { osip_message_free (answer); eXosip_call_send_answer (ctx, evt->tid, 415, NULL); } else eXosip_call_send_answer (ctx, evt->tid, 200, answer); } eXosip_unlock (ctx); ~~~~~~~ <b>Note</b>: In the above code, you can note that to send a response to a request, you have to use the tid (transaction identifier) and not a cid (call identifier) or a did (dialog identifier). <b>Note2</b>: For sending a 200ok, you'll usually need to insert a SDP body in the answer and before this, to negotiate the parameters and codecs that you want to support. This is left to you! Once you have created the SDP, you add it in the answer using the following code: ~~~~~~~{.c} osip_message_set_body (answer, tmp, strlen (tmp)); osip_message_set_content_type (answer, "application/sdp"); ~~~~~~~ <H2>Terminate a Call</H2> Simple API, no much to say about it! You can use it when you want: it will either send a CANCEL, a negative answer or a BYE depending on the call state. ~~~~~~~{.c} eXosip_lock (ctx); eXosip_call_terminate (ctx, cid, did); eXosip_unlock (ctx); ~~~~~~~ <b>Note</b>: You can't stop a call where no 100 Trying has been received. In that case, you need to wait before sending a CANCEL or a BYE... This is per rfc3261. <H2>Sending INFO, REFER, UPDATE, NOTIFY, OPTIONS request</H2> The call control API allows you to send and receive REFER, UPDATE, INFO, OPTIONS, NOTIFY and INVITEs whitin calls. Here you have a code sample to send an INFO requests used to send an out of band dtmf within the signalling layer. (not standard, but still used on some system!) ~~~~~~~{.c} osip_message_t *info; char dtmf_body[1000]; int i; eXosip_lock (ctx); i = eXosip_call_build_info (ctx, evt->did, &info); if (i == 0) { snprintf (dtmf_body, 999, "Signal=%c\r\nDuration=250\r\n", c); osip_message_set_content_type (info, "application/dtmf-relay"); osip_message_set_body (info, dtmf_body, strlen (dtmf_body)); i = eXosip_call_send_request (ctx, evt->did, info); } eXosip_unlock (ctx); ~~~~~~~ <H2>Sending any other request, with any header</H2> You can in fact, send any kind of other request using eXosip2 API. You will find many other API to build any kind of sip message. Using osip API, you can add any header or body in those message. eXosip2 will always prepare the minimal and technical stuff you need. ~~~~~~~{.c} osip_message_t *message; char body[1000]; int i; eXosip_lock (ctx); i = eXosip_call_build_request (ctx, evt->did, "PRIVATECOMMAND", &message); if (i == 0) { snprintf (body, 999, "room=1;light=on\r\nroom=2;light=off\r\n"); osip_message_set_content_type (message, "application/antisip-domotic"); osip_message_set_body (message, body, strlen (body)); osip_message_set_header (invite, "P-MyCommand", "option=value"); i = eXosip_call_send_request (ctx, evt->did, message); } eXosip_unlock (ctx); ~~~~~~~ */
4. libexosip2_user_agent.c (libexosip2-5.1.0/tools/sip_reg.c add register and invite)
/* * SIP Registration Agent -- by ww@styx.org * * This program is Free Software, released under the GNU General * Public License v2.0 http://www.gnu.org/licenses/gpl * * This program will register to a SIP proxy using the contact * supplied on the command line. This is useful if, for some * reason your SIP client cannot register to the proxy itself. * For example, if your SIP client registers to Proxy A, but * you want to be able to recieve calls that arrive at Proxy B, * you can use this program to register the client's contact * information to Proxy B. * * This program requires the eXosip library. To compile, * assuming your eXosip installation is in /usr/local, * use something like: * * gcc -O2 -I/usr/local/include -L/usr/local/lib sipreg.c \ * -o sipreg \ * -leXosip2 -losip2 -losipparser2 -lpthread * * It should compile and run on any POSIX compliant system * that supports pthreads. * */ #if defined(__arc__) #define LOG_PERROR 1 #include <includes_api.h> #include <os_cfg_pub.h> #endif #if !defined(WIN32) && !defined(_WIN32_WCE) #include <stdio.h> #include <stdlib.h> #include <unistd.h> #include <sys/types.h> #include <sys/socket.h> #include <netinet/in.h> #include <netdb.h> #include <syslog.h> #ifndef OSIP_MONOTHREAD #include <pthread.h> #endif #include <string.h> #ifdef __linux #include <signal.h> #endif #endif #ifdef _WIN32_WCE /* #include <syslog.h> */ #include <winsock2.h> #endif #include <osip2/osip_mt.h> #include <eXosip2/eXosip.h> #if !defined(WIN32) && !defined(_WIN32_WCE) && !defined(__arc__) #define _GNU_SOURCE #include <getopt.h> #endif #define PROG_NAME "sipreg" #define PROG_VER "1.0" #define UA_STRING "SipReg v" PROG_VER #define SYSLOG_FACILITY LOG_DAEMON ; #ifdef __linux static void intHandler (int dummy) { keepRunning = ; } #endif #if defined(WIN32) || defined(_WIN32_WCE) static void syslog_wrapper (int a, const char *fmt, ...) { va_list args; va_start (args, fmt); vfprintf (stdout, fmt, args); va_end (args); } #define LOG_INFO 0 #define LOG_ERR 0 #define LOG_WARNING 0 #define LOG_DEBUG 0 #elif defined(LOG_PERROR) /* If we can, we use syslog() to emit the debugging messages to stderr. */ //#define syslog_wrapper syslog #define syslog_wrapper(a,b...) printf(b);printf("\n") #else #define syslog_wrapper(a,b...) fprintf(stderr,b);fprintf(stderr,"\n") #endif static void usage (void); static void usage (void) { printf ("Usage: " PROG_NAME " [required_options] [optional_options]\n" "\n\t[required_options]\n" "\t-r --proxy\tsip:proxyhost[:port]\n" "\t-u --from\tsip:user@host[:port]\n" "\n\t[optional_options]\n" "\t-d --debug (log to stderr and do not fork)\n" "\t-h --help\n" "\n\t[optional_sip_options]\n" "\t-U --username\tauthentication username\n" "\t-P --password\tauthentication password\n" "\t-t --transport\tUDP|TCP|TLS|DTLS (default UDP)\n" "\t-e --expiry\tnumber (default 3600)\n" "\n\t[very_optional_sip_options]\n" "\t-p --port\tnumber (default 5060)\n" "\t-c --contact\tsip:user@host[:port]\n" "\t-f --firewallip\tN.N.N.N\n" "\t-l --localip\tN.N.N.N (force local IP address)\n"); } typedef struct regparam_t { int regid; int expiry; int auth; } regparam_t; struct eXosip_t *context_eXosip; ; /*zhoudd add ui session process*/ static void *ui_session_proc(void *arg) { printf("r register\n"); printf("u unregister\n"); printf("i call\n"); printf("h hangup\n"); printf("a answer\n"); printf("q quit\n"); printf("f info\n"); printf("m message\n"); char c; for(;;) { syslog_wrapper (LOG_INFO, "Please input the command:\n"); if(scanf("%c",&c)){ getchar(); } switch(c) { case 'r': break; case 'u': break; case 'i': { osip_message_t *invite; int cid; int i; i = eXosip_call_build_initial_invite (context_eXosip, &invite, "sip:1000@182.61.147.213", "sip:1002@182.61.147.213", NULL, // optional route header "This is a call for a conversation"); ) { syslog_wrapper (LOG_ERR, "eXosip_call_build_initial_invite failure"); } osip_message_set_supported (invite, "100rel"); { ]; ]; eXosip_guess_localip (context_eXosip, AF_INET, localip, ); snprintf (tmp, , "v=0\r\n" "o=jack 0 0 IN IP4 %s\r\n" "s=conversation\r\n" "c=IN IP4 %s\r\n" "t=0 0\r\n" "m=audio %d RTP/AVP 0 8 101\r\n" "a=rtpmap:0 PCMU/8000\r\n" "a=rtpmap:8 PCMA/8000\r\n" "a=rtpmap:101 telephone-event/8000\r\n" "a=fmtp:101 0-11\r\n", localip, localip, rtp_port); osip_message_set_body (invite, tmp, strlen (tmp)); osip_message_set_content_type (invite, "application/sdp"); } eXosip_lock (context_eXosip); cid = eXosip_call_send_initial_invite (context_eXosip, invite); ) { //eXosip_call_set_reference (context_eXosip, i, reference); } eXosip_unlock (context_eXosip); break; } case 'h': break; case 'f': break; case 'm': break; case 'q': { exit (); break; } case 'a': break; default: break; } } return NULL; } #ifdef _WIN32_WCE int WINAPI WinMain (HINSTANCE hInstance, HINSTANCE hPrevInstance, LPTSTR lpCmdLine, int nCmdShow) #else int main (int argc, char *argv[]) #endif { int c; ; char *contact = NULL; char *fromuser = NULL; const char *localip = NULL; const char *firewallip = NULL; char *proxy = NULL; ]; #if !defined(__arc__) struct servent *service; #endif char *username = NULL; char *password = NULL; , , }; ; ; int err; #ifdef __linux signal (SIGINT, intHandler); #endif snprintf (transport, sizeof (transport), "%s", "UDP"); #ifdef _WIN32_WCE proxy = osip_strdup ("sip:sip.antisip.com"); fromuser = osip_strdup ("sip:jack@sip.antisip.com"); #else for (;;) { #define short_options "du:r:U:P:t:p:c:e:f:l:h" #ifdef _GNU_SOURCE ; static struct option long_options[] = { {"debug", no_argument, NULL, 'd'}, {"from", required_argument, NULL, 'u'}, {"proxy", required_argument, NULL, 'r'}, {"username", required_argument, NULL, 'U'}, {"password", required_argument, NULL, 'P'}, {"transport", required_argument, NULL, 't'}, {"port", required_argument, NULL, 'p'}, {"contact", required_argument, NULL, 'c'}, {"expiry", required_argument, NULL, 'e'}, {"firewallip", required_argument, NULL, 'f'}, {"localip", required_argument, NULL, 'l'}, {"help", no_argument, NULL, 'h'}, {NULL, , NULL, } }; c = getopt_long (argc, argv, short_options, long_options, &option_index); #else c = getopt (argc, argv, short_options); #endif ) break; switch (c) { case 'c': contact = optarg; break; case 'd': nofork = ; #ifdef LOG_PERROR debug = LOG_PERROR; #endif break; case 'e': regparam.expiry = atoi (optarg); break; case 'f': firewallip = optarg; break; case 'h': usage (); exit (); case 'l': localip = optarg; break; case 'p': #if !defined(__arc__) service = getservbyname (optarg, "udp"); if (service) port = ntohs (service->s_port); else port = atoi (optarg); #else port = atoi (optarg); #endif break; case 't': snprintf (transport, sizeof (transport), "%s", optarg); break; case 'r': proxy = optarg; break; case 'u': fromuser = optarg; break; case 'U': username = optarg; break; case 'P': password = optarg; break; default: break; } } #endif if (!proxy || !fromuser) { usage (); exit (); } #ifndef _WIN32_WCE if (!nofork) { daemon (, ); } #endif #if 0 openlog (PROG_NAME, LOG_PID | debug, SYSLOG_FACILITY); #endif syslog_wrapper (LOG_INFO, UA_STRING " up and running"); syslog_wrapper (LOG_INFO, "proxy: %s", proxy); syslog_wrapper (LOG_INFO, "fromuser: %s", fromuser); syslog_wrapper (LOG_INFO, "contact: %s", contact); syslog_wrapper (LOG_INFO, "expiry: %d", regparam.expiry); syslog_wrapper (LOG_INFO, "local port: %d", port); syslog_wrapper (LOG_INFO, "transport: %s", transport); && osip_strcasecmp (transport, && osip_strcasecmp (transport, && osip_strcasecmp (transport, ) { syslog_wrapper (LOG_ERR, "wrong transport parameter"); usage (); exit (); } ) TRACE_INITIALIZE (, NULL); context_eXosip = eXosip_malloc (); if (eXosip_init (context_eXosip)) { syslog_wrapper (LOG_ERR, "eXosip_init failed"); exit (); } err = -; ) { err = eXosip_listen_addr (context_eXosip, IPPROTO_UDP, NULL, port, AF_INET, ); } ) { err = eXosip_listen_addr (context_eXosip, IPPROTO_TCP, NULL, port, AF_INET, ); } ) { err = eXosip_listen_addr (context_eXosip, IPPROTO_TCP, NULL, port, AF_INET, ); } ) { err = eXosip_listen_addr (context_eXosip, IPPROTO_UDP, NULL, port, AF_INET, ); } if (err) { syslog_wrapper (LOG_ERR, "eXosip_listen_addr failed"); exit (); } if (localip) { syslog_wrapper (LOG_INFO, "local address: %s", localip); eXosip_masquerade_contact (context_eXosip, localip, port); } if (firewallip) { syslog_wrapper (LOG_INFO, "firewall address: %s:%i", firewallip, port); eXosip_masquerade_contact (context_eXosip, firewallip, port); } eXosip_set_user_agent (context_eXosip, UA_STRING); if (username && password) { syslog_wrapper (LOG_INFO, "username: %s", username); syslog_wrapper (LOG_INFO, "password: [removed]"); if (eXosip_add_authentication_info (context_eXosip, username, username, password, NULL, NULL)) { syslog_wrapper (LOG_ERR, "eXosip_add_authentication_info failed"); exit (); } } { osip_message_t *reg = NULL; int i; regparam.regid = eXosip_register_build_initial_register (context_eXosip, fromuser, proxy, contact, regparam.expiry * , ®); ) { syslog_wrapper (LOG_ERR, "eXosip_register_build_initial_register failed"); exit (); } i = eXosip_register_send_register (context_eXosip, regparam.regid, reg); ) { syslog_wrapper (LOG_ERR, "eXosip_register_send_register failed"); exit (); } } pthread_t ui_session_id; ){ syslog_wrapper (LOG_ERR, "pthread_create ui_session_proc failed"); exit (); } for (; keepRunning;) { ; eXosip_event_t *event; counter++; == ) { struct eXosip_stats stats; memset (&stats, , sizeof (struct eXosip_stats)); eXosip_lock (context_eXosip); eXosip_set_option (context_eXosip, EXOSIP_OPT_GET_STATISTICS, &stats); eXosip_unlock (context_eXosip); syslog_wrapper (LOG_INFO, "eXosip stats: inmemory=(tr:%i//reg:%i) average=(tr:%f//reg:%f)", stats.allocated_transactions, stats.allocated_registrations, stats.average_transactions, stats.average_registrations); } , ))) { #ifdef OSIP_MONOTHREAD eXosip_execute (context_eXosip); #endif eXosip_automatic_action (context_eXosip); osip_usleep (); continue; } #ifdef OSIP_MONOTHREAD eXosip_execute (context_eXosip); #endif eXosip_lock (context_eXosip); eXosip_automatic_action (context_eXosip); switch (event->type) { case EXOSIP_REGISTRATION_SUCCESS: syslog_wrapper (LOG_INFO, "registrered successfully"); break; case EXOSIP_REGISTRATION_FAILURE: syslog_wrapper (LOG_INFO, "registrered failure"); break; case EXOSIP_CALL_INVITE: { syslog_wrapper (LOG_INFO, "call invite"); if(MSG_IS_INVITE(event->request)) { osip_body_t *body; osip_message_get_body (, &body); syslog_wrapper (LOG_INFO, "body: %s\n", body->body); } osip_message_t *answer = NULL; int i; /*180 ring*/ i = eXosip_call_build_answer (context_eXosip, , &answer); ) { syslog_wrapper (LOG_ERR, "failed to reject %s", event->request->sip_method); break; } i = eXosip_call_send_answer (context_eXosip, , answer); ) { syslog_wrapper (LOG_ERR, "failed to reject %s", event->request->sip_method); break; } /*200 ok*/ i = eXosip_call_build_answer (context_eXosip, , &answer); ) { eXosip_call_send_answer (context_eXosip, , NULL); } else { #if 0 i = sdp_complete_200ok (event->did, answer); ) { osip_message_free (answer); eXosip_call_send_answer (context_eXosip, , NULL); } #else { ]; ]; eXosip_guess_localip (context_eXosip, AF_INET, localip, ); snprintf (tmp, , "v=0\r\n" "o=jack 0 0 IN IP4 %s\r\n" "s=conversation\r\n" "c=IN IP4 %s\r\n" "t=0 0\r\n" "m=audio %d RTP/AVP 0 8 101\r\n" "a=rtpmap:0 PCMU/8000\r\n" "a=rtpmap:8 PCMA/8000\r\n" "a=rtpmap:101 telephone-event/8000\r\n" "a=fmtp:101 0-11\r\n", localip, localip, rtp_port); osip_message_set_body (answer, tmp, strlen (tmp)); osip_message_set_content_type (answer, "application/sdp"); } eXosip_call_send_answer (context_eXosip, , answer); #endif } break; } case EXOSIP_MESSAGE_NEW: { syslog_wrapper (LOG_INFO, "message new"); syslog_wrapper (LOG_INFO, "<<< %s", event->request->sip_method); syslog_wrapper (LOG_INFO, "<<< %s", event->request->message); break; } case EXOSIP_IN_SUBSCRIPTION_NEW: { syslog_wrapper (LOG_INFO, "in subscription new"); osip_message_t *answer; int i; i = eXosip_insubscription_build_answer (context_eXosip, , &answer); ) { syslog_wrapper (LOG_ERR, "failed to reject %s", event->request->sip_method); break; } i = eXosip_insubscription_send_answer (context_eXosip, , answer); ) { syslog_wrapper (LOG_ERR, "failed to reject %s", event->request->sip_method); break; } syslog_wrapper (LOG_INFO, "%s rejected with 405", event->request->sip_method); break; } case EXOSIP_CALL_CANCELLED: syslog_wrapper (LOG_INFO, "call cancelled"); break; case EXOSIP_CALL_ACK: { osip_message_t *answer; syslog_wrapper (LOG_INFO, "call ack"); eXosip_call_build_ack (context_eXosip, event->did, &answer); eXosip_call_send_ack (context_eXosip, event->did, answer); break; } case EXOSIP_CALL_CLOSED: syslog_wrapper (LOG_INFO, "call closed"); break; case EXOSIP_CALL_PROCEEDING: syslog_wrapper (LOG_INFO, "call proceeding"); break; case EXOSIP_CALL_RINGING: syslog_wrapper (LOG_INFO, "call ringing"); break; case EXOSIP_CALL_ANSWERED: syslog_wrapper (LOG_INFO, "call answered"); syslog_wrapper (LOG_INFO, "<<< %s", event->request->message); break; case EXOSIP_CALL_REQUESTFAILURE: syslog_wrapper (LOG_INFO, "call requestfailure"); break; case EXOSIP_CALL_MESSAGE_NEW: { syslog_wrapper (LOG_INFO, "call message new"); syslog_wrapper (LOG_INFO, "<<< %s", event->request->sip_method); break; } case EXOSIP_CALL_MESSAGE_ANSWERED: { syslog_wrapper (LOG_INFO, "call message answered"); syslog_wrapper (LOG_INFO, "<<< %s", event->request->message); break; } case EXOSIP_CALL_SERVERFAILURE: case EXOSIP_CALL_RELEASED: syslog_wrapper (LOG_INFO, "call release"); break; default: syslog_wrapper (LOG_DEBUG, "recieved unknown eXosip event (type, did, cid) = (%d, %d, %d)", event->type, event->did, event->cid); } eXosip_unlock (context_eXosip); eXosip_event_free (event); } eXosip_quit (context_eXosip); osip_free (context_eXosip); ; }
caller mode(gb28181 server)
dong@ubuntu:~/doing/demo$ ./test.sh SipReg v1. up and running proxy: sip: fromuser: sip:@182.61.147.213 contact: sip:@ expiry: local port: transport: UDP username: password: [removed] r register u unregister i call h hangup a answer q quit f info m message Please input the command: registrered failure registrered successfully registrered successfully registrered successfully i Please input the command: call proceeding call requestfailure call proceeding call ringing call answered call message new BYE recieved message eXosip event (type, did, cid) = (, , ) call closed call release Please input the command: q dong@ubuntu:~/doing/demo$
callee mode (gb28181 device)
dong@ubuntu:~/doing/demo$ ./test.sh SipReg v1. up and running proxy: sip: fromuser: sip:@182.61.147.213 contact: sip:@ expiry: local port: transport: UDP username: password: [removed] r register u unregister i call h hangup a answer q quit f info m message Please input the command: registrered failure registrered successfully registrered successfully registrered successfully call invite call cancelled call closed Please input the command: call release q dong@ubuntu:~/doing/demo$
IV. GB28181 RTP Media Channel
1、sip信令转入rtp媒体流
信令通道接通了就可以转到媒体通道,媒体通道处理设备的音视频ps流可以有两种方案
1)在fs里定制mod-gb28181模块,因为fs里已经有rtp收发模块,改造这个就行。
前面帮一个深圳的大佬在fs里倒腾了个mod-gb28181模块,他是通信行业的,不熟悉媒体流,折腾了一好阵子终于弄出来了,咱也不好意思让他拿出来共享呀
我倒是熟悉媒体流,但是不熟悉fs,现在也没空折腾,所以还没办法在fs的rtp通道定制ps解封装模块。
2)fs只作为信令服务器,rtp流不走fs,使用Bypass模式将rtp流调转到我们自定义的rtp服务器,rtp服务器就好做了,用ffmpeg解封装ps流拿到音视频数据,再自行处理。
fs的Bypass模式配置参考如下
FreeSWITCH Explained / Configuration / Proxy Media
https://www.cnblogs.com/dong1/p/10693993.html
2、ps流封装和解封装
可以用ffmpeg封装和解封装ps流,源码包里有两个示例既可以将h264/aac/g711封装成ps,又可以将ps解封装成h264/aac/g711
ffmpeg-4.1/doc/examples/remuxing.c
ffmpeg-4.1/doc/examples/transcoding.c
另外也有人整理了个ps封装库,经验证是靠谱的
https://github.com/shenshuyu/es2ps
提供个测试demo,填EsFrame , 回调函数出ps
void PsMuxerCb(char* psData, unsigned long dataLen, unsigned int lastPackMark){ } int main(int argc, char **argv) { EsFrame *frame = (EsFrame *)malloc(sizeof(EsFrame)); MuxerHandle m = create_ps_muxer(PsMuxerCb, NULL); input_es_frame(m, frame); release_ps_muxer(m); free(frame); return 0; }
3、轻便的gb28181协议中的rtp+ps格式视频流的封装和解析
有网友用go写了个更简单的,我没有验证,因为我们前面这套系统是c/c++开发的,后面的服务器程序应该会全面转向go.
https://github.com/max-min/streams
媒体通道我还没有整合完,工作太忙,这套系统比前面在公司做的那套更好,有时间就来倒腾几下,直到满意为止!
V. GB28181服务器的级联
因为freeswitch的sip级联用起来非常轻松,所以sip信令服务器没有比fs更好的了。
FreeSwitch Proxy + RTPProxy Media server
https://www.cnblogs.com/dong1/p/10529462.html
Go on in spare time ...
Simple GB28181 System的更多相关文章
- Building simple plug-ins system for ASP.NET Core(转)
Recently I built plug-ins support to my TemperatureStation IoT solution web site. The code for .NET ...
- Simple File System
This is my operating system class design. scanner.h #include<string> using namespace std; #pra ...
- Artix-7 50T FPGA试用笔记之Create a simple MicroBlaze System
前言:之前笔者的试用博文提到安富利这块板子非常适合MicroBlaze开发,同时网上关于MicroBlaze的资料非常少(或含糊不清),没有一篇能完整介绍VIVADO SDK的设计流程,所以笔者带来这 ...
- Simple Live System Using Nginx
1. Install nginx #Preinstalled directory install=/usr/local/nginx #Delete installed directory rm -rf ...
- PAT1129:Recommendation System
1129. Recommendation System (25) 时间限制 400 ms 内存限制 65536 kB 代码长度限制 16000 B 判题程序 Standard 作者 CHEN, Yue ...
- A1129. Recommendation System
Recommendation system predicts the preference that a user would give to an item. Now you are asked t ...
- PAT A1129 Recommendation System (25 分)——set,结构体重载小于号
Recommendation system predicts the preference that a user would give to an item. Now you are asked t ...
- 1129 Recommendation System
1129 Recommendation System (25 分) Recommendation system predicts the preference that a user would gi ...
- PAT甲级 1129. Recommendation System (25)
1129. Recommendation System (25) 时间限制 400 ms 内存限制 65536 kB 代码长度限制 16000 B 判题程序 Standard 作者 CHEN, Yue ...
随机推荐
- Git 使用的问题总结
1.git stash pop 显示 xxx already exists, no checkout 当我们先使用 git stash save -u '保存信息说明' 来储藏更改,然后拉取代码 gi ...
- php实现大视频上传
本人在2010年时使用swfupload为核心进行文件的批量上传的解决方案.见文章:WEB版一次选择多个文件进行批量上传(swfupload)的解决方案. 本人在2013年时使用plupload为核心 ...
- 校赛 你的粪坑V2
原题 今天举办程序设计比赛,2点30分开始,然而你睡到了2点25分,紧张的你将头发梳成大人模样,敷上一层最贵的面膜,穿着滑板鞋,以飞一般的速度奔向计算机学院准备参加程序设计竞赛!冠军是你的! 然而路上 ...
- [design pattern](1) Strategy
引言 最近,在学习设计模式相关的知识.本博客主要想讲一讲策略模式,这也是我学习的第一个模式.写下这篇博客,主要想记录下个人的一点理解,也是想通过写博客的方式来加深对与Strategy的一点理解.以下的 ...
- iOS9 3DTouch 之 Home Screen Quick Actions
最后更新:2016-12-18 测试环境: Xcode8.1 一.前言 iOS9 已经过去一年了,3D Touch也在项目中实战过,但一直没有总结一下,现在新的项目也用到了3D Touch, 网上找了 ...
- ASYNC_NETWORK_IO等待事件和调优
测试反应测试数据库整体出现hang的情况,检查对应的等待事件,发现大量的resource_semaphore等待事件, 查看内存占用情况: SELECT * FROM sys.dm_exe ...
- VMware 虚拟化编程(4) — VDDK 安装
目录 目录 前文列表 VDDK 安装 VDDK 前文列表 VMware 虚拟化编程(1) - VMDK/VDDK/VixDiskLib/VADP 概念简析 VMware 虚拟化编程(2) - 虚拟磁盘 ...
- 自动化生成 Openstack 新项目开发框架
目录 目录 前言 环境 openstack-project-generator 前言 Openstack Developer 应该都知道, 开发一个 Openstack 的新项目并不是一个从 0 到 ...
- Linux pip命令报错 -bash: pip: command not found
下载 $ wget https://bootstrap.pypa.io/get-pip.py 安装 $ python get-pip.py 查看版本 $ pip -V 查看安装路径: find / - ...
- 【ABAP系列】SAP VA02修改销售订单的BAPI举例
公众号:SAP Technical 本文作者:matinal 原文出处:http://www.cnblogs.com/SAPmatinal/ 原文链接:[ABAP系列]SAP VA02修改销售订单的B ...