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 ...
随机推荐
- POJ 2229 sumset ( 完全背包 || 规律递推DP )
题意 : 给出一个数 n ,问如果使用 2 的幂的和来组成这个数 n 有多少种不同的方案? 分析 : 完全背包解法 将问题抽象==>有重量分别为 2^0.2^1.2^2…2^k 的物品且每种物 ...
- 【CF1237C】Balanced Removals(降维)
题意:三维平面上有n个点,每个点的坐标为(x[i],y[i],z[i]),n为偶数 现在要求取n/2次,每次取走一对点(x,y),要求没有未被取走的点在以x和y为对角点的矩形中 要求给出任意一组合法方 ...
- border、outline、boxshadow那些事
border 边框是我们美化网页.增强样式最常用的手段之一.例如: <div class="text"></div> .text { width: 254p ...
- React Native商城项目实战10 - 个人中心中间内容设置
1.新建一个MineMiddleView.js,专门用于构建中间的内容 /** * 个人中心中间内容设置 */ import React, { Component } from 'react'; im ...
- win10 安装了virtualBox 启动报错 rc=-5640
刚刚安装的virtualbox 启动就弹窗报错,,但是通过找到log 发现了下面这样的一处报错: 然后搜素发现是跟win10应用程序错误,,解决办法如下: 1.到控制面板,,--程序 再次尝试,vir ...
- customizable route planning 工业界地图产品的路径规划
https://www.microsoft.com/en-us/research/publication/customizable-route-planning/?from=http%3A%2F%2F ...
- 测开之路九十七:js的引用方式
第一种:引用外部js文件 准备一个js文件 <!-- 引用外部的js --><script src="../js/js01.js"></script& ...
- loadrunner+win2003虚拟机的安装
lr11的安装和使用 准备材料: 1.win2003镜像下载 2.虚拟机下载 3.lr11的下载 一.创建win2003虚拟机 打开虚拟机,选择win2003系统镜像,输入密钥(可百度),用户名密码( ...
- 【HANA系列】SAP HANA 1.0 SPS 11 新特性
公众号:SAP Technical 本文作者:matinal 原文出处:http://www.cnblogs.com/SAPmatinal/ 原文链接:[HANA系列]SAP HANA 1.0 SPS ...
- Java 实现Excel的简单读取操作
JAVA实现Excel表单的简单读取操作 实现Excel表单的简单读取操作,首先要导入相关的jar包: 如图所示: 此处贴上代码: public static List<List<Stri ...