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 ...
随机推荐
- 20180827(02)- Java发送邮件
Java 发送邮件 使用Java应用程序发送E-mail十分简单,但是首先你应该在你的机器上安装JavaMail API 和Java Activation Framework (JAF) . 你可以在 ...
- [CSP-S模拟测试]:f(Trie树+二分答案+meet in middle+two pointers)
题目传送门(内部题67) 输入格式 第一行,三个整数$n$.$k$.$p$.第二行,$n$个自然数,表示$\{a_i\}$. 输出格式 输出一行,两个自然数,表示$f(res)$.$res$. 样例 ...
- jni中arm64-v8a,armeabi-v7a,armeabi文件夹的意义和用法<转>
jni中arm64-v8a,armeabi-v7a,armeabi文件夹的意义和用法 起因 之前并没有关注这块,直到:您的应用被拒绝,原因:xplay5sQ心里点击笑值点击拍照显示停止运行,查看发过来 ...
- P1080国王游戏
传送 最大值最小什么的一看就是二分了qwq 然鹅并不知道怎么检查,所以我们换个思路 我们要求出最小的最大值,这肯定和大臣的排列有关,会不会有什么规律? 先看看只有两个大臣的情况 排列:1 2,ans1 ...
- Log4net记录日志到本地或数据库
OperatorLog /****** Object: Table [dbo].[OperatorLog] Script Date: SET ANSI_NULLS ON GO SET QUOTED_I ...
- java对接短信平台
短信验证码目前是比较主流验证身份的一种方式,下面分享下我对接的几种短信平台 阿里云短信:https://api.alidayu.com/docs/api.htm?spm=a3142.7395905.4 ...
- 阶段1 语言基础+高级_1-3-Java语言高级_04-集合_02 泛型_3_定义和使用含有泛型的类
创建一个类,添加一个name的属性,然后生成get和set 使用上面创建的类 使用泛型 所以我们取出来也是一个Object的类型 定义的时候规定的类型是Integer,所以这里setName设置的时候 ...
- excel实现筛选去重操作
前情提要: 做图表时,希望更新数据后能自动化更新图表,需要各种公式之间相互配合.此时的需求是,将A表中的不同用户登录的地点做一个图表统计. 1.创建透视表 以用户id和地点当做行标签制作透视表,透视表 ...
- Http Handler 介绍
引言 在 Part.1 Http请求处理流程 一文中,我们了解了Http请求的处理过程以及其它一些运作原理.我们知道Http管道中有两个可用接口,一个是IHttpHandler,一个是IHttpMod ...
- 应用安全-Web安全-SSRF攻防
原理 服务器: IP:.XX.191.14 nc -l -p 客户端: http://xx.map.xx.com/maps/services/thumbnails?width=215&heig ...