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 ...
随机推荐
- AGC030F - Permutation and Minimum
https://atcoder.jp/contests/agc030/tasks/agc030_f 题解 我们先把这个排列从\(1 \sim 2n\)表达出来,然后题面中的每一对数我们可以用一条线把他 ...
- [CSP-S模拟测试]:e(树上主席树)
题目传送门(内部题66) 输入格式 第一行,一个正整数$n$,一个自然数$q$,一个整数$type$.第二行,$n$个正整数,代表$a_i$.接下来$n-1$行,每行两个正整数$u$.$v$,代表树中 ...
- HDU 6592 (LIS+输出字典序最大最小)
题意:给你一个序列,让你找长度最长的字典序最小和最大的单峰序列,单峰序列就是满足先增后降的序列. 思路:先正着求一遍LIS,再反着求一遍LIS,然后用单调栈来模拟. 求字典序最小的话,首先找到第一个顶 ...
- Linux添加目录到环境变量以及添加Sublime Text到环境变量
本文主要介绍了Linux添加目录到环境变量以及添加Sublime Text到环境变量,通过具体的解释说明,让我们从中学到Linux添加目录到环境变量以及添加Sublime Text到环境变量的精髓所在 ...
- 解决Delphi窗体缩放の疑难杂症
http://anony3721.blog.163.com/blog/static/511974201082235754423/ 解决Delphi窗体缩放の疑难杂症 2010-09-22 15:57: ...
- Winform 开源控件库( Sheng.Winform.Controls)
升讯威 .Net WinForm 控件库提供了超过15种 Winform 控件,你可以直接使用本控件库,更可以通过本控件库学到 Winform 控件开发的方法和理念. 你可以学习到: 如何基于 Con ...
- Leaflet
https://leafletjs.com/ https://github.com/Leaflet/Leaflet
- C# 模拟登陆
原理 我们知道,一般需要登录的网站,服务器和客户端都会有一段时间的会话保持,而这个会话保持是在登录时候建立的, 服务端和客户端都会持有这个KEY,在后续访问时,都需要核对这两个KEY是否一致. 而客户 ...
- 学习《Oracle PL/SQL 实例讲解 原书第5版》----创建账户
通过readme.pdf创建student账户. 以下用sys账户登录时都是sysdba. 一.PL/SQL 登录oracle. SYS/123 AS SYSDBA 账户名:sys:密码:123:作 ...
- Java判断一个类里是否存在某个属性
Java判断一个类里是否存在某个属性 测试pojo类,比方我有个User类 @Getter @Setter public class User { private Long id; private S ...