Browse code

rtp_media_server: adding module

Julien Chavanton authored on 19/10/2018 23:46:55
Showing 15 changed files
... ...
@@ -207,6 +207,9 @@ mod_list_rabbitmq=rabbitmq
207 207
 # - modules depending on libphonenumber library
208 208
 mod_list_phonenum=phonenum
209 209
 
210
+# - modules depending on oRTP and mediastreamer2 libraries
211
+mod_list_rtp_media=rtp_media_server
212
+
210 213
 # - all modules
211 214
 mod_list_all=$(sort $(mod_list_basic) $(mod_list_extra) \
212 215
 			   $(mod_list_db) $(mod_list_dbuid) \
... ...
@@ -239,7 +242,8 @@ mod_list_all=$(sort $(mod_list_basic) $(mod_list_extra) \
239 242
 			   $(mod_list_erlang) $(mod_list_systemd) \
240 243
 			   $(mod_list_http_async) $(mod_list_nsq) \
241 244
 			   $(mod_list_rabbitmq) $(mod_list_jsdt)) \
242
-			   $(mod_list_phonenum)
245
+			   $(mod_list_phonenum) \
246
+			   $(mod_list_rtp_media)
243 247
 
244 248
 
245 249
 
246 250
new file mode 100644
... ...
@@ -0,0 +1,13 @@
1
+include ../../Makefile.defs
2
+auto_gen=
3
+NAME=rtp_media_server.so
4
+
5
+DEFS+=-I$(LOCALBASE)/lib
6
+
7
+ORTPLIBS=-lortp
8
+BCUNITLIBS=-lbcunit
9
+MS2LIBS=-lmediastreamer_voip -lmediastreamer_base
10
+
11
+LIBS=$(ORTPLIBS) $(BCUNITLIBS) $(MS2LIBS)
12
+DEFS+=-DKAMAILIO_MOD_INTERFACE
13
+include ../../Makefile.modules
0 14
new file mode 100755
... ...
@@ -0,0 +1,56 @@
1
+
2
+debug=3
3
+log_stderror=yes
4
+memdbg=5
5
+memlog=5
6
+
7
+# number of SIP routing processes
8
+children=5
9
+
10
+loadmodule "ctl"
11
+loadmodule "tm"
12
+loadmodule "tmx"
13
+loadmodule "sl"
14
+loadmodule "rr"
15
+loadmodule "pv"
16
+loadmodule "textops"
17
+loadmodule "siputils"
18
+loadmodule "xlog"
19
+loadmodule "nathelper"
20
+loadmodule "rtp_media_server"
21
+modparam("rtp_media_server", "log_file_name", "/tmp/rms_transfer.log")
22
+modparam("tm", "wt_timer", 1000)
23
+listen=udp:147.75.39.121:5090
24
+
25
+event_route[rms:start] {
26
+	xnotice("[rms:start] play ...\n");
27
+	rms_play("./voice_file/Bach_10s_8000.wav", "rms:after_play");
28
+};
29
+
30
+event_route[rms:after_play] {
31
+	xnotice("[rms:after_play] play done...\n");
32
+	rms_hangup();
33
+};
34
+
35
+route {
36
+	if (t_precheck_trans()) {
37
+		t_check_trans();
38
+		exit;
39
+	}
40
+	t_check_trans();
41
+
42
+	xnotice("[$rm][$ci]\n");
43
+	if (is_method("INVITE") && !has_totag()) {
44
+		fix_nated_contact();
45
+		if (!rms_answer()) {
46
+			t_reply("503", "server error");
47
+			xerr("rtp_media_server error!\n");
48
+			exit;
49
+		}
50
+	}
51
+	if (is_method("BYE")){
52
+		rms_media_stop();
53
+	}
54
+	exit;
55
+}
56
+
0 57
new file mode 100644
... ...
@@ -0,0 +1,4 @@
1
+docs = rtp_media_server.xml
2
+
3
+docbook_dir = ../../../../doc/docbook
4
+include $(docbook_dir)/Makefile.module
0 5
new file mode 100644
... ...
@@ -0,0 +1,42 @@
1
+<?xml version="1.0" encoding='ISO-8859-1'?>
2
+<!DOCTYPE book PUBLIC "-//OASIS//DTD DocBook XML V4.4//EN"
3
+"http://www.oasis-open.org/docbook/xml/4.4/docbookx.dtd" [
4
+
5
+<!-- Include general documentation entities -->
6
+<!ENTITY % docentities SYSTEM "../../../../doc/docbook/entities.xml">
7
+%docentities;
8
+
9
+]>
10
+
11
+<book xmlns:xi="http://www.w3.org/2001/XInclude">
12
+	<bookinfo>
13
+	<title>rtp_media_server Module</title>
14
+	<productname class="trade">&kamailio;</productname>
15
+	<authorgroup>
16
+        <author>
17
+        <firstname>Julien</firstname>
18
+        <surname>Chavanton</surname>
19
+        <email>jchavanton@gmail.com</email>
20
+        </author>
21
+		<author>
22
+		<firstname>Julien</firstname>
23
+		<surname>Chavanton</surname>
24
+		<affiliation><orgname>flowroute.com</orgname></affiliation>
25
+		<email>jchavanton@gmail.com</email>
26
+		</author>
27
+		<editor>
28
+		<firstname>Julien</firstname>
29
+		<surname>Chavanton</surname>
30
+		<affiliation><orgname>flowroute.com</orgname></affiliation>
31
+		<email>jchavanton@gmail.com</email>
32
+		</editor>
33
+	</authorgroup>
34
+	<copyright>
35
+		<year>2017-2018</year>
36
+		<holder>Flowroute.com</holder>
37
+	</copyright>
38
+	</bookinfo>
39
+	<toc></toc>
40
+
41
+	<xi:include href="rtp_media_server_admin.xml"/>
42
+</book>
0 43
new file mode 100644
... ...
@@ -0,0 +1,193 @@
1
+<?xml version="1.0" encoding='ISO-8859-1'?>
2
+<!DOCTYPE book PUBLIC "-//OASIS//DTD DocBook XML V4.4//EN"
3
+"http://www.oasis-open.org/docbook/xml/4.4/docbookx.dtd" [
4
+
5
+<!-- Include general documentation entities -->
6
+<!ENTITY % docentities SYSTEM "../../../../doc/docbook/entities.xml">
7
+%docentities;
8
+
9
+]>
10
+
11
+<chapter>
12
+	<title>&adminguide;</title>
13
+
14
+<section>
15
+	<title>Overview</title>
16
+	<para>
17
+		rtp_media_server module is adding RTP and media processing functionalities to Kamailio
18
+	</para>
19
+	<para>
20
+		Kamailio is providing SIP signaling including and enpoint with Dialog state, SDP parsing and scripting language
21
+	</para><para>
22
+		oRTP: is providing Real-time Transport Protocol (RFC 3550)
23
+	</para><para>
24
+		mediastreamer2: is providing mediaprocessing functionnalities using graphs and filters, many modules are available
25
+		to support various features, it should be relatively simple to integrated them.
26
+	</para><para>
27
+		mediastreamer2 is also providing a framework to create custom mediaprocessing modules.
28
+	</para>
29
+</section>
30
+
31
+<section>
32
+	<title>Dependencies</title>
33
+	<section>
34
+		<title>&kamailio; Modules</title>
35
+		<para>
36
+		The module depends on the following modules (in the other words
37
+		the listed modules must be loaded before this module):
38
+		<itemizedlist>
39
+			<listitem>
40
+			<para><emphasis>tm</emphasis> - accounting module</para>
41
+			</listitem>
42
+		</itemizedlist>
43
+		</para>
44
+	</section>
45
+	<section>
46
+		<title>External Libraries or Applications</title>
47
+		<para>
48
+		The following libraries or applications must be installed
49
+		before running &kamailio; with this module loaded:
50
+		</para><para>
51
+		If you want to build oRTP and mediastreamer from source, you can use the provided script for Debian "install_bc.sh".
52
+		</para>
53
+		<itemizedlist>
54
+			<listitem>
55
+			<para>
56
+				<emphasis>oRTP</emphasis> git://git.linphone.org/ortp.git
57
+			</para>
58
+			<para>
59
+				oRTP is a library implemeting Real-time Transport Protocol (RFC 3550), distributed under GNU GPLv2 or proprietary license.
60
+			</para>
61
+			</listitem>
62
+			<listitem>
63
+			<para>
64
+				<emphasis>mediastreamer2</emphasis> git clone git://git.linphone.org/mediastreamer2.git
65
+			</para>
66
+			<para>
67
+				Mediastreamer2 is a powerful and lightweight streaming engine specialized for voice/video telephony applications.
68
+			</para>
69
+			</listitem>
70
+		</itemizedlist>
71
+	</section>
72
+</section>
73
+
74
+<section>
75
+	<title>Parameters</title>
76
+	<section>
77
+	<title><varname>log_file_name</varname> (string)</title>
78
+		<para>
79
+		oRTP and MediaStreamer2 log file settings
80
+		the log mask is not configurable :
81
+		MESSAGE | WARNING | ERROR | FATAL
82
+		levels are activated.
83
+		</para>
84
+		<para>
85
+		Default value is not-set (no logging to file).
86
+		</para>
87
+		<example>
88
+		<title>log_file_name example</title>
89
+		<programlisting format="linespecific">
90
+...
91
+modparam("rtp_media_server", "log_file_name", "/var/log/rms/rms_ortp.log")
92
+...
93
+		</programlisting></example>
94
+	</section>
95
+</section>
96
+
97
+<section>
98
+	<title>Functions</title>
99
+
100
+	<section id="rtp_media_server.f.rms_answer">
101
+		<title><varname>rms_answer</varname> ()</title>
102
+		<para>
103
+		Create a session and a call leg and call the event_route[rms:start]
104
+		config example
105
+		</para>
106
+		<example>
107
+		<title>usage example</title>
108
+		<programlisting format="linespecific">
109
+...
110
+event_route[rms:start] {
111
+	xnotice("[rms:start] play ...\n");
112
+	rms_play("/tmp/reference_8000.wav", "rms:after_play");
113
+};
114
+
115
+event_route[rms:after_play] {
116
+	xnotice("[rms:after_play] play done...\n");
117
+	rms_hangup();
118
+};
119
+
120
+route {
121
+	if (t_precheck_trans()) {
122
+		t_check_trans();
123
+		exit;
124
+	}
125
+	t_check_trans();
126
+	if (is_method("INVITE") &amp;&amp; !has_totag()) {
127
+		if (!rms_answer()) {
128
+			t_reply("503", "server error");
129
+		}
130
+	}
131
+
132
+	if (is_method("BYE")){
133
+		xnotice("BYE RECEIVED [$ci]\n");
134
+		rms_media_stop();
135
+	}
136
+...
137
+		</programlisting></example>
138
+	</section>
139
+
140
+	<section id="rtp_media_server.f.rms_hangup">
141
+		<title><varname>rms_hangup</varname> ()</title>
142
+		<para>
143
+		Send a BYE, delete the RTP session and the media ressources.
144
+		</para>
145
+		<example>
146
+		<title>usage example</title>
147
+		<programlisting format="linespecific">
148
+...
149
+	rms_hangup();
150
+...
151
+		</programlisting></example>
152
+	</section>
153
+
154
+	<section id="rtp_media_server.f.rms_media_stop">
155
+		<title><varname>rms_media_stop</varname> ()</title>
156
+		<para>
157
+		This should be called on reception of a BYE, this will
158
+		delete the RTP session and the media ressources.
159
+		and reply "200 OK".
160
+		</para>
161
+		<para>
162
+		If the SIP session is not found "481 Call/Transaction Does Not Exist"
163
+		is returned.
164
+		</para>
165
+		<example>
166
+		<title>usage example</title>
167
+		<programlisting format="linespecific">
168
+...
169
+	if (is_method("BYE")){
170
+		rms_media_stop();
171
+	}
172
+...
173
+		</programlisting></example>
174
+	</section>
175
+
176
+	<section id="rtp_media_server.f.rms_play">
177
+		<title><varname>rms_play</varname> ()</title>
178
+		<para>
179
+		Play a wav file, a resampler is automaticaly configured to resample
180
+		and convert stereo to mono if needed.
181
+		</para><para>
182
+		The second parameter is the event route that will be called when the file was played.
183
+		</para>
184
+		<example>
185
+		<title>usage example</title>
186
+		<programlisting format="linespecific">
187
+...
188
+	rms_play("file.wav", "event_route_name");
189
+...
190
+		</programlisting></example>
191
+	</section>
192
+</section>
193
+</chapter>
0 194
new file mode 100755
... ...
@@ -0,0 +1,45 @@
1
+# https://github.com/jchavanton/rtp_media_server.git
2
+apt-get install automake autogen autoconf libtool pkg-config
3
+# bcunit
4
+git clone https://github.com/BelledonneCommunications/bcunit.git
5
+cd bcunit
6
+git checkout 29c556fa8ac1ab21fba1291231ffa8dea43cf32a
7
+./autogen.sh
8
+./configure
9
+make
10
+make install
11
+cd ..
12
+
13
+# bctoolbox
14
+apt-get install libmbedtls-dev
15
+git clone https://github.com/BelledonneCommunications/bctoolbox.git
16
+cd bctoolbox
17
+git checkout 971953a9fa4058e9c8a40ca4a3fa12d832445255
18
+./autogen.sh
19
+./configure
20
+make
21
+make install
22
+cd ..
23
+
24
+# oRTP
25
+git clone https://github.com/BelledonneCommunications/ortp.git
26
+git checkout 6e13ef49a55cdd19dae395c38cfff7ffa518a089
27
+cd ortp
28
+./autogen.sh
29
+./configure
30
+make
31
+make install
32
+cd ..
33
+
34
+# mediastreamer2
35
+apt-get install intltool libspeex-dev libspeexdsp-dev
36
+git clone https://github.com/BelledonneCommunications/mediastreamer2.git
37
+cd mediastreamer2
38
+git checkout d935123fc497d19a24019c6e7ae4fe0c5f19d55a
39
+./autogen.sh
40
+./configure --disable-sound --disable-video --enable-tools=no --disable-tests
41
+make
42
+make install
43
+cd ..
44
+
45
+ldconfig
0 46
new file mode 100644
... ...
@@ -0,0 +1,319 @@
1
+/*
2
+ * Copyright (C) 2017-2018 Julien Chavanton jchavanton@gmail.com
3
+ *
4
+ * This file is part of Kamailio, a free SIP server.
5
+ *
6
+ * Kamailio is free software; you can redistribute it and/or modify
7
+ * it under the terms of the GNU General Public License as published by
8
+ * the Free Software Foundation; either version 2 of the License, or
9
+ * (at your option) any later version
10
+ *
11
+ * Kamailio is distributed in the hope that it will be useful,
12
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
13
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14
+ * GNU General Public License for more details.
15
+ *
16
+ * You should have received a copy of the GNU General Public License
17
+ * along with this program; if not, write to the Free Software
18
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301 USA
19
+ */
20
+
21
+#include "../../core/mem/shm.h"
22
+#include "../../core/sr_module.h"
23
+#include "rtp_media_server.h"
24
+
25
+inline static void *ptr_shm_malloc(size_t size)
26
+{
27
+	return shm_malloc(size);
28
+}
29
+inline static void *ptr_shm_realloc(void *ptr, size_t size)
30
+{
31
+	return shm_realloc(ptr, size);
32
+}
33
+inline static void ptr_shm_free(void *ptr)
34
+{
35
+	shm_free(ptr);
36
+}
37
+
38
+typedef struct shared_global_vars
39
+{
40
+	MSFactory *ms_factory;
41
+	gen_lock_t lock;
42
+} shared_global_vars_t;
43
+
44
+static shared_global_vars_t *vars;
45
+
46
+MSFilterDesc *rms_ms_filter_descs[] = {&ms_alaw_dec_desc, &ms_alaw_enc_desc,
47
+		&ms_ulaw_dec_desc, &ms_ulaw_enc_desc, &ms_rtp_send_desc,
48
+		&ms_rtp_recv_desc, &ms_dtmf_gen_desc, &ms_volume_desc,
49
+		&ms_equalizer_desc, &ms_channel_adapter_desc, &ms_audio_mixer_desc,
50
+		&ms_tone_detector_desc, &ms_speex_dec_desc, &ms_speex_enc_desc,
51
+		&ms_speex_ec_desc, &ms_file_player_desc, &ms_file_rec_desc,
52
+		&ms_resample_desc,
53
+		//       &ms_opus_dec_desc,
54
+		//       &ms_opus_enc_desc,
55
+		NULL};
56
+
57
+static MSFactory *rms_create_factory()
58
+{
59
+	MSFactory *f = ms_factory_new();
60
+	int i;
61
+	for(i = 0; rms_ms_filter_descs[i] != NULL; i++) {
62
+		ms_factory_register_filter(f, rms_ms_filter_descs[i]);
63
+	}
64
+	ms_factory_init_plugins(f);
65
+	ms_factory_enable_statistics(f, TRUE);
66
+	ms_factory_reset_statistics(f);
67
+	return f;
68
+}
69
+
70
+int rms_media_init()
71
+{
72
+	OrtpMemoryFunctions ortp_memory_functions;
73
+	ortp_memory_functions.malloc_fun = ptr_shm_malloc;
74
+	ortp_memory_functions.realloc_fun = ptr_shm_realloc;
75
+	ortp_memory_functions.free_fun = ptr_shm_free;
76
+	ortp_set_memory_functions(&ortp_memory_functions);
77
+	ortp_init();
78
+	vars = shm_malloc(sizeof(shared_global_vars_t));
79
+	return 1;
80
+}
81
+
82
+static MSTicker *rms_create_ticker(char *name)
83
+{
84
+	MSTickerParams params;
85
+	params.name = name;
86
+	params.prio = MS_TICKER_PRIO_NORMAL;
87
+	return ms_ticker_new_with_params(&params);
88
+}
89
+
90
+void rms_media_destroy(call_leg_media_t *m)
91
+{
92
+	LM_INFO("rtp_session_destroy[%p]\n", m->rtps);
93
+	rtp_session_destroy(m->rtps);
94
+	m->rtps = NULL;
95
+	LM_INFO("ms_ticker[%p]\n", m->ms_ticker);
96
+	ms_ticker_destroy(m->ms_ticker);
97
+	m->ms_ticker = NULL;
98
+	LM_INFO("ms_factory_destroy[%p]\n", m->ms_factory);
99
+	ms_factory_destroy(m->ms_factory);
100
+	m->ms_factory = NULL;
101
+}
102
+
103
+int create_call_leg_media(call_leg_media_t *m)
104
+{
105
+	m->ms_factory = rms_create_factory();
106
+	// create caller RTP session
107
+	LM_INFO("RTP session [%s:%d]<>[%s:%d]\n", m->local_ip.s, m->local_port,
108
+			m->remote_ip.s, m->remote_port);
109
+	m->rtps = ms_create_duplex_rtp_session(m->local_ip.s, m->local_port,
110
+			m->local_port + 1, ms_factory_get_mtu(m->ms_factory));
111
+	rtp_session_set_remote_addr_full(m->rtps, m->remote_ip.s, m->remote_port,
112
+			m->remote_ip.s, m->remote_port + 1);
113
+	rtp_session_set_payload_type(m->rtps, m->pt->type);
114
+	rtp_session_enable_rtcp(m->rtps, FALSE);
115
+	// create caller filters : rtprecv1/rtpsend1/encoder1/decoder1
116
+	m->ms_rtprecv = ms_factory_create_filter(m->ms_factory, MS_RTP_RECV_ID);
117
+	m->ms_rtpsend = ms_factory_create_filter(m->ms_factory, MS_RTP_SEND_ID);
118
+
119
+	LM_INFO("codec[%s]\n", m->pt->mime_type);
120
+	m->ms_encoder = ms_factory_create_encoder(m->ms_factory, m->pt->mime_type);
121
+	if(!m->ms_encoder) {
122
+		LM_ERR("creating encoder failed.\n");
123
+		return 0;
124
+	}
125
+	m->ms_decoder = ms_factory_create_decoder(m->ms_factory, m->pt->mime_type);
126
+
127
+	/* set filter params */
128
+	ms_filter_call_method(m->ms_rtpsend, MS_RTP_SEND_SET_SESSION, m->rtps);
129
+	ms_filter_call_method(m->ms_rtprecv, MS_RTP_RECV_SET_SESSION, m->rtps);
130
+	return 1;
131
+}
132
+
133
+int rms_bridge(call_leg_media_t *m1, call_leg_media_t *m2)
134
+{
135
+	MSConnectionHelper h;
136
+	m1->ms_ticker = rms_create_ticker(NULL);
137
+
138
+	// direction 1
139
+	ms_connection_helper_start(&h);
140
+	ms_connection_helper_link(&h, m1->ms_rtprecv, -1, 0);
141
+	ms_connection_helper_link(&h, m2->ms_rtpsend, 0, -1);
142
+
143
+	// direction 2
144
+	ms_connection_helper_start(&h);
145
+	ms_connection_helper_link(&h, m2->ms_rtprecv, -1, 0);
146
+	ms_connection_helper_link(&h, m1->ms_rtpsend, 0, -1);
147
+
148
+	ms_ticker_attach_multiple(
149
+			m1->ms_ticker, m1->ms_rtprecv, m2->ms_rtprecv, NULL);
150
+
151
+	return 1;
152
+}
153
+
154
+static void rms_player_eof(
155
+		void *user_data, MSFilter *f, unsigned int event, void *event_data)
156
+{
157
+	if(event == MS_FILE_PLAYER_EOF) {
158
+		rms_session_info_t *si = (rms_session_info_t *)user_data;
159
+		si->action = RMS_DONE;
160
+	}
161
+	MS_UNUSED(f), MS_UNUSED(event_data);
162
+}
163
+
164
+int rms_stop_bridge(call_leg_media_t *m1, call_leg_media_t *m2)
165
+{
166
+	MSConnectionHelper h;
167
+	if(!m1->ms_ticker)
168
+		return -1;
169
+	if(m1->ms_rtpsend)
170
+		ms_ticker_detach(m1->ms_ticker, m1->ms_rtpsend);
171
+	if(m1->ms_rtprecv)
172
+		ms_ticker_detach(m1->ms_ticker, m1->ms_rtprecv);
173
+	if(m2->ms_rtpsend)
174
+		ms_ticker_detach(m1->ms_ticker, m2->ms_rtpsend);
175
+	if(m2->ms_rtprecv)
176
+		ms_ticker_detach(m1->ms_ticker, m2->ms_rtprecv);
177
+	rtp_stats_display(rtp_session_get_stats(m1->rtps),
178
+			" AUDIO BRIDGE offer RTP STATISTICS ");
179
+	rtp_stats_display(rtp_session_get_stats(m2->rtps),
180
+			" AUDIO BRIDGE answer RTP STATISTICS ");
181
+	ms_factory_log_statistics(m1->ms_factory);
182
+
183
+	ms_connection_helper_start(&h);
184
+	if(m1->ms_rtprecv)
185
+		ms_connection_helper_unlink(&h, m1->ms_rtprecv, -1, 0);
186
+	if(m2->ms_rtpsend)
187
+		ms_connection_helper_unlink(&h, m2->ms_rtpsend, 0, -1);
188
+
189
+	ms_connection_helper_start(&h);
190
+	if(m2->ms_rtprecv)
191
+		ms_connection_helper_unlink(&h, m2->ms_rtprecv, -1, 0);
192
+	if(m1->ms_rtpsend)
193
+		ms_connection_helper_unlink(&h, m1->ms_rtpsend, 0, -1);
194
+
195
+	if(m1->ms_rtpsend)
196
+		ms_filter_destroy(m1->ms_rtpsend);
197
+	if(m1->ms_rtprecv)
198
+		ms_filter_destroy(m1->ms_rtprecv);
199
+	if(m2->ms_rtpsend)
200
+		ms_filter_destroy(m2->ms_rtpsend);
201
+	if(m2->ms_rtprecv)
202
+		ms_filter_destroy(m2->ms_rtprecv);
203
+	return 1;
204
+}
205
+
206
+int rms_playfile(call_leg_media_t *m, char *file_name)
207
+{
208
+	int file_sample_rate = 8000;
209
+	if(!m->ms_player)
210
+		return 0;
211
+	ms_filter_add_notify_callback(m->ms_player, rms_player_eof, m->si, TRUE);
212
+	ms_filter_call_method(m->ms_player, MS_FILE_PLAYER_OPEN, (void *)file_name);
213
+	ms_filter_call_method(m->ms_player, MS_FILE_PLAYER_START, NULL);
214
+	ms_filter_call_method(m->ms_player, MS_FILTER_GET_SAMPLE_RATE, &file_sample_rate);
215
+	ms_filter_call_method(m->ms_resampler, MS_FILTER_SET_SAMPLE_RATE, &file_sample_rate);
216
+	ms_filter_call_method(m->ms_resampler, MS_FILTER_SET_OUTPUT_SAMPLE_RATE, &m->pt->clock_rate);
217
+	ms_filter_call_method(m->ms_resampler, MS_FILTER_SET_OUTPUT_NCHANNELS, &m->pt->channels);
218
+	LM_INFO("clock[%d][%d]\n", m->pt->clock_rate, file_sample_rate);
219
+	return 1;
220
+}
221
+
222
+int rms_start_media(call_leg_media_t *m, char *file_name)
223
+{
224
+	MSConnectionHelper h;
225
+	int channels = 1;
226
+	int file_sample_rate = 8000;
227
+	m->ms_ticker = rms_create_ticker(NULL);
228
+	if(!m->ms_ticker) goto error;
229
+	m->ms_player = ms_factory_create_filter(m->ms_factory, MS_FILE_PLAYER_ID);
230
+	if(!m->ms_player) goto error;
231
+	m->ms_resampler = ms_factory_create_filter(m->ms_factory, MS_RESAMPLE_ID);
232
+	if(!m->ms_resampler) goto error;
233
+	// m->ms_recorder = ms_factory_create_filter(m->ms_factory,
234
+	// MS_FILE_PLAYER_ID);
235
+	m->ms_voidsink = ms_factory_create_filter(m->ms_factory, MS_VOID_SINK_ID);
236
+	if(!m->ms_voidsink) goto error;
237
+	LM_INFO("m[%p]call-id[%p]\n", m, m->si->callid.s);
238
+
239
+	ms_filter_call_method(
240
+			m->ms_player, MS_FILTER_SET_OUTPUT_NCHANNELS, &channels);
241
+	ms_filter_call_method_noarg(m->ms_player, MS_FILE_PLAYER_START);
242
+	ms_filter_call_method(m->ms_player, MS_FILTER_GET_SAMPLE_RATE, &file_sample_rate);
243
+	ms_filter_call_method(m->ms_resampler, MS_FILTER_SET_SAMPLE_RATE, &file_sample_rate);
244
+	ms_filter_call_method(m->ms_resampler, MS_FILTER_SET_OUTPUT_SAMPLE_RATE, &m->pt->clock_rate);
245
+
246
+	// sending graph
247
+	ms_connection_helper_start(&h);
248
+	ms_connection_helper_link(&h, m->ms_player, -1, 0);
249
+
250
+	ms_connection_helper_link(&h, m->ms_resampler, 0, 0);
251
+	ms_connection_helper_link(&h, m->ms_encoder, 0, 0);
252
+	ms_connection_helper_link(&h, m->ms_rtpsend, 0, -1);
253
+
254
+	// receiving graph
255
+	ms_connection_helper_start(&h);
256
+	ms_connection_helper_link(&h, m->ms_rtprecv, -1, 0);
257
+	// ms_connection_helper_link(&h, m->ms_decoder, 0, 0);
258
+	ms_connection_helper_link(&h, m->ms_voidsink, 0, -1);
259
+
260
+	ms_ticker_attach_multiple(m->ms_ticker, m->ms_player, m->ms_rtprecv, NULL);
261
+	return 1;
262
+error:
263
+	LM_ERR(" can not start media!\n");
264
+	return 0;
265
+}
266
+
267
+int rms_stop_media(call_leg_media_t *m)
268
+{
269
+	MSConnectionHelper h;
270
+	if(!m->ms_ticker) {
271
+		LM_ERR("RMS STOP MEDIA\n");
272
+		return -1;
273
+	}
274
+	if(m->ms_player)
275
+		ms_ticker_detach(m->ms_ticker, m->ms_player);
276
+	if(m->ms_rtprecv)
277
+		ms_ticker_detach(m->ms_ticker, m->ms_rtprecv);
278
+
279
+	rtp_stats_display(
280
+			rtp_session_get_stats(m->rtps), " AUDIO SESSION'S RTP STATISTICS ");
281
+	ms_factory_log_statistics(m->ms_factory);
282
+
283
+	/*dismantle the sending graph*/
284
+	ms_connection_helper_start(&h);
285
+	if(m->ms_player)
286
+		ms_connection_helper_unlink(&h, m->ms_player, -1, 0);
287
+	if(m->ms_resampler)
288
+		ms_connection_helper_unlink(&h, m->ms_resampler, 0, 0);
289
+	if(m->ms_encoder)
290
+		ms_connection_helper_unlink(&h, m->ms_encoder, 0, 0);
291
+	if(m->ms_rtpsend)
292
+		ms_connection_helper_unlink(&h, m->ms_rtpsend, 0, -1);
293
+	/*dismantle the receiving graph*/
294
+	ms_connection_helper_start(&h);
295
+	if(m->ms_rtprecv)
296
+		ms_connection_helper_unlink(&h, m->ms_rtprecv, -1, 0);
297
+	if(m->ms_voidsink)
298
+		ms_connection_helper_unlink(&h, m->ms_voidsink, 0, -1);
299
+
300
+	if(m->ms_player)
301
+		ms_filter_destroy(m->ms_player);
302
+	if(m->ms_resampler)
303
+		ms_filter_destroy(m->ms_resampler);
304
+	if(m->ms_encoder)
305
+		ms_filter_destroy(m->ms_encoder);
306
+	if(m->ms_rtpsend) {
307
+		LM_ERR("detroy rtpsend\n");
308
+		ms_filter_destroy(m->ms_rtpsend);
309
+	} else {
310
+		LM_ERR("no rtpsend\n");
311
+	}
312
+	if(m->ms_rtprecv)
313
+		ms_filter_destroy(m->ms_rtprecv);
314
+	if(m->ms_voidsink)
315
+		ms_filter_destroy(m->ms_voidsink);
316
+
317
+	rms_media_destroy(m);
318
+	return 1;
319
+}
0 320
new file mode 100644
... ...
@@ -0,0 +1,101 @@
1
+/*
2
+ * Copyright (C) 2017-2018 Julien Chavanton jchavanton@gmail.com
3
+ *
4
+ * This file is part of Kamailio, a free SIP server.
5
+ *
6
+ * Kamailio is free software; you can redistribute it and/or modify
7
+ * it under the terms of the GNU General Public License as published by
8
+ * the Free Software Foundation; either version 2 of the License, or
9
+ * (at your option) any later version
10
+ *
11
+ * Kamailio is distributed in the hope that it will be useful,
12
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
13
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14
+ * GNU General Public License for more details.
15
+ *
16
+ * You should have received a copy of the GNU General Public License
17
+ * along with this program; if not, write to the Free Software
18
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
19
+ */
20
+
21
+#ifndef rms_media_h
22
+#define rms_media_h
23
+
24
+#include "../../core/mem/shm.h"
25
+#include <mediastreamer2/mediastream.h>
26
+#include <mediastreamer2/msrtp.h>
27
+#include <mediastreamer2/dtmfgen.h>
28
+#include <mediastreamer2/msfileplayer.h>
29
+#include <mediastreamer2/msfilerec.h>
30
+#include <mediastreamer2/msrtp.h>
31
+#include <mediastreamer2/mstonedetector.h>
32
+#include <mediastreamer2/msfilter.h>
33
+#include <mediastreamer2/mscommon.h>
34
+#include <ortp/ortp.h>
35
+#include <ortp/port.h>
36
+
37
+struct rms_session_info;
38
+
39
+typedef struct call_leg_media
40
+{
41
+	MSFactory *ms_factory;
42
+	RtpSession *rtps;
43
+	PayloadType *pt;
44
+	MSTicker *ms_ticker;
45
+	MSFilter *ms_encoder;
46
+	MSFilter *ms_decoder;
47
+	MSFilter *ms_rtprecv;
48
+	MSFilter *ms_rtpsend;
49
+	MSFilter *ms_resampler;
50
+	MSFilter *ms_player;
51
+	MSFilter *ms_recorder;
52
+	MSFilter *ms_dtmfgen;
53
+	MSFilter *ms_tonedet;
54
+	MSFilter *ms_voidsource;
55
+	MSFilter *ms_voidsink;
56
+	str local_ip;
57
+	int local_port;
58
+	str remote_ip;
59
+	int remote_port;
60
+	struct rms_session_info *si;
61
+} call_leg_media_t;
62
+
63
+int create_call_leg_media(call_leg_media_t *m);
64
+
65
+int rms_media_init();
66
+void rms_media_destroy();
67
+
68
+MSFactory *rms_get_factory();
69
+
70
+int rms_stop_media(call_leg_media_t *m);
71
+int rms_playfile(call_leg_media_t *m, char *file_name);
72
+int rms_start_media(call_leg_media_t *m, char *file_name);
73
+int rms_bridge(call_leg_media_t *m1, call_leg_media_t *m2);
74
+int rms_stop_bridge(call_leg_media_t *m1, call_leg_media_t *m2);
75
+
76
+extern MSFilterDesc ms_pcap_file_player_desc;
77
+extern MSFilterDesc ms_rtp_send_desc;
78
+extern MSFilterDesc ms_rtp_recv_desc;
79
+extern MSFilterDesc ms_udp_send_desc;
80
+extern MSFilterDesc ms_alaw_dec_desc;
81
+extern MSFilterDesc ms_alaw_enc_desc;
82
+extern MSFilterDesc ms_ulaw_dec_desc;
83
+extern MSFilterDesc ms_ulaw_enc_desc;
84
+extern MSFilterDesc ms_dtmf_gen_desc;
85
+extern MSFilterDesc ms_volume_desc;
86
+extern MSFilterDesc ms_equalizer_desc;
87
+extern MSFilterDesc ms_channel_adapter_desc;
88
+extern MSFilterDesc ms_audio_mixer_desc;
89
+extern MSFilterDesc ms_tone_detector_desc;
90
+extern MSFilterDesc ms_genericplc_desc;
91
+extern MSFilterDesc ms_file_player_desc;
92
+extern MSFilterDesc ms_file_rec_desc;
93
+extern MSFilterDesc ms_vad_dtx_desc;
94
+extern MSFilterDesc ms_speex_dec_desc;
95
+extern MSFilterDesc ms_speex_enc_desc;
96
+extern MSFilterDesc ms_speex_ec_desc;
97
+extern MSFilterDesc ms_opus_enc_desc;
98
+extern MSFilterDesc ms_opus_dec_desc;
99
+extern MSFilterDesc ms_resample_desc;
100
+
101
+#endif
0 102
new file mode 100644
... ...
@@ -0,0 +1,291 @@
1
+/*
2
+ * Copyright (C) 2017-2018 Julien Chavanton jchavanton@gmail.com
3
+ *
4
+ * This file is part of Kamailio, a free SIP server.
5
+ *
6
+ * Kamailio is free software; you can redistribute it and/or modify
7
+ * it under the terms of the GNU General Public License as published by
8
+ * the Free Software Foundation; either version 2 of the License, or
9
+ * (at your option) any later version
10
+ *
11
+ * Kamailio is distributed in the hope that it will be useful,
12
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
13
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14
+ * GNU General Public License for more details.
15
+ *
16
+ * You should have received a copy of the GNU General Public License
17
+ * along with this program; if not, write to the Free Software
18
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301 USA
19
+ */
20
+
21
+#include "rms_sdp.h"
22
+#include "rms_util.h"
23
+#include "../../core/data_lump.h"
24
+#include "../../core/parser/parse_content.h"
25
+
26
+// https://tools.ietf.org/html/rfc4566
27
+// (protocol version)
28
+const char *sdp_v = "v=0\r\n";
29
+// (session name)
30
+const char *sdp_s = "s=-\r\n";
31
+// (time the session is active)
32
+const char *sdp_t = "t=0 0\r\n";
33
+//"a=rtpmap:101 telephone-event/8000\r\n"
34
+//"a=fmtp:101 0-15\r\n";
35
+//"a=rtpmap:0 PCMU/8000\r\n"
36
+//"a=rtpmap:8 PCMA/8000\r\n"
37
+//"a=rtpmap:96 opus/48000/2\r\n"
38
+//"a=fmtp:96 useinbandfec=1\r\n";
39
+
40
+static char *rms_sdp_get_rtpmap(str body, int type_number)
41
+{
42
+	char *pos = body.s;
43
+	while((pos = strstr(pos, "a=rtpmap:"))) {
44
+		int id;
45
+		int sampling_rate;
46
+		char codec[64];
47
+		sscanf(pos, "a=rtpmap:%d %s/%d", &id, codec, &sampling_rate);
48
+		if(id == type_number) {
49
+			LM_INFO("[%d][%s/%d]\n", id, codec, sampling_rate);
50
+			return rms_char_dup(codec, 1);
51
+		}
52
+		pos++;
53
+	}
54
+	return NULL;
55
+}
56
+
57
+void rms_sdp_info_init(rms_sdp_info_t *sdp_info)
58
+{
59
+	memset(sdp_info, 0, sizeof(rms_sdp_info_t));
60
+}
61
+
62
+void rms_sdp_info_free(rms_sdp_info_t *sdp_info)
63
+{
64
+	if(sdp_info->remote_ip.s) {
65
+		shm_free(sdp_info->remote_ip.s);
66
+		sdp_info->remote_ip.s = NULL;
67
+	}
68
+	if(sdp_info->payloads.s) {
69
+		shm_free(sdp_info->payloads.s);
70
+		sdp_info->payloads.s = NULL;
71
+	}
72
+	if(sdp_info->new_body.s) {
73
+		shm_free(sdp_info->new_body.s);
74
+		sdp_info->new_body.s = NULL;
75
+	}
76
+}
77
+
78
+int rms_sdp_prepare_new_body(rms_sdp_info_t *sdp_info, int payload_type_number)
79
+{
80
+	if(sdp_info->new_body.s)
81
+		return 0;
82
+
83
+	str *body = &sdp_info->new_body;
84
+	body->len = strlen(sdp_v) + strlen(sdp_s) + strlen(sdp_t);
85
+
86
+	// (originator and session identifier)
87
+	char sdp_o[128];
88
+	snprintf(
89
+			sdp_o, 128, "o=- 1028316687 1 IN IP4 %s\r\n", sdp_info->local_ip.s);
90
+	body->len += strlen(sdp_o);
91
+
92
+	// (connection information -- not required if included in all media)
93
+	char sdp_c[128];
94
+	snprintf(sdp_c, 128, "c=IN IP4 %s\r\n", sdp_info->local_ip.s);
95
+	body->len += strlen(sdp_c);
96
+
97
+	char sdp_m[128];
98
+	snprintf(sdp_m, 128, "m=audio %d RTP/AVP %d\r\n", sdp_info->udp_local_port,
99
+			payload_type_number);
100
+	body->len += strlen(sdp_m);
101
+
102
+	body->s = pkg_malloc(body->len + 1);
103
+	if (!body->s) return 0;
104
+	strcpy(body->s, sdp_v);
105
+	strcat(body->s, sdp_o);
106
+	strcat(body->s, sdp_s);
107
+	strcat(body->s, sdp_c);
108
+	strcat(body->s, sdp_t);
109
+	strcat(body->s, sdp_m);
110
+	return 1;
111
+}
112
+
113
+PayloadType *rms_sdp_check_payload(rms_sdp_info_t *sdp)
114
+{
115
+	// https://tools.ietf.org/html/rfc3551
116
+	LM_INFO("payloads[%s]\n", sdp->payloads.s); // 0 8
117
+	PayloadType *pt = payload_type_new();
118
+	char *payloads = sdp->payloads.s;
119
+	char *payload_type_number = strtok(payloads, " ");
120
+	if(!payload_type_number) {
121
+		payload_type_destroy(pt);
122
+		return NULL;
123
+	}
124
+	pt->type = atoi(payload_type_number);
125
+	pt->clock_rate = 8000;
126
+	pt->channels = 1;
127
+	pt->mime_type = NULL;
128
+	while(!pt->mime_type) {
129
+		if(pt->type > 127) {
130
+			return NULL;
131
+			//		} else if (pt->type >= 96) {
132
+			//			continue;
133
+			//			char *rtpmap =
134
+			// rms_sdp_get_rtpmap(sdp->recv_body, pt->type);
135
+			//			pt->mime_type = rms_char_dup(strtok(rtpmap,
136
+			//"/"),1);
137
+			//			if (strcasecmp(pt->mime_type,"opus") == 0) {
138
+			//				pt->clock_rate = atoi(strtok(NULL,
139
+			//"/"));
140
+			//				pt->channels = atoi(strtok(NULL, "/"));
141
+			//				shm_free(rtpmap);
142
+			//				return pt;
143
+			//			}
144
+			//			shm_free(pt->mime_type);
145
+			//			pt->mime_type=NULL;
146
+			//			shm_free(rtpmap);
147
+		} else if(pt->type == 0) {
148
+			pt->mime_type = rms_char_dup("pcmu", 1); /* ia=rtpmap:0 PCMU/8000*/
149
+		} else if(pt->type == 8) {
150
+			pt->mime_type = rms_char_dup("pcma", 1);
151
+			//		} else if (pt->type == 9) {
152
+			//			pt->mime_type=rms_char_dup("g722", 1);
153
+			//		} else if (pt->type == 18) {
154
+			//			pt->mime_type=rms_char_dup("g729", 1);
155
+		}
156
+		if(pt->mime_type)
157
+			break;
158
+		payload_type_number = strtok(NULL, " ");
159
+		if(!payload_type_number) {
160
+			payload_type_destroy(pt);
161
+			return NULL;
162
+		}
163
+		pt->type = atoi(payload_type_number);
164
+	}
165
+	LM_INFO("payload_type:%d %s/%d/%d\n", pt->type, pt->mime_type,
166
+			pt->clock_rate, pt->channels);
167
+	return pt;
168
+}
169
+
170
+
171
+
172
+int rms_sdp_set_body(struct sip_msg *msg, str *new_body)
173
+{
174
+	struct lump *anchor;
175
+	char *buf;
176
+	int len;
177
+	char *value_s;
178
+	int value_len;
179
+	str body = {0, 0};
180
+	str content_type_sdp = str_init("application/sdp");
181
+
182
+	if(!new_body->s || new_body->len == 0) {
183
+		LM_ERR("invalid body parameter\n");
184
+		return -1;
185
+	}
186
+
187
+	body.len = 0;
188
+	body.s = get_body(msg);
189
+	if(body.s == 0) {
190
+		LM_ERR("malformed sip message\n");
191
+		return -1;
192
+	}
193
+
194
+	del_nonshm_lump(&(msg->body_lumps));
195
+	msg->body_lumps = NULL;
196
+
197
+	if(msg->content_length) {
198
+		body.len = get_content_length(msg);
199
+		if(body.len > 0) {
200
+			if(body.s + body.len > msg->buf + msg->len) {
201
+				LM_ERR("invalid content length: %d\n", body.len);
202
+				return -1;
203
+			}
204
+			if(del_lump(msg, body.s - msg->buf, body.len, 0) == 0) {
205
+				LM_ERR("cannot delete existing body");
206
+				return -1;
207
+			}
208
+		}
209
+	}
210
+
211
+	anchor = anchor_lump(msg, msg->unparsed - msg->buf, 0, 0);
212
+	if(!anchor) {
213
+		LM_ERR("failed to get anchor\n");
214
+		return -1;
215
+	}
216
+
217
+	if(msg->content_length == 0) {
218
+		/* need to add Content-Length */
219
+		len = new_body->len;
220
+		value_s = int2str(len, &value_len);
221
+		LM_DBG("content-length: %d (%s)\n", value_len, value_s);
222
+
223
+		len = CONTENT_LENGTH_LEN + value_len + CRLF_LEN;
224
+		buf = pkg_malloc(sizeof(char) * (len));
225
+		if(!buf) {
226
+			LM_ERR("out of pkg memory\n");
227
+			return -1;
228
+		}
229
+
230
+		memcpy(buf, CONTENT_LENGTH, CONTENT_LENGTH_LEN);
231
+		memcpy(buf + CONTENT_LENGTH_LEN, value_s, value_len);
232
+		memcpy(buf + CONTENT_LENGTH_LEN + value_len, CRLF, CRLF_LEN);
233
+		if(insert_new_lump_after(anchor, buf, len, 0) == 0) {
234
+			LM_ERR("failed to insert content-length lump\n");
235
+			pkg_free(buf);
236
+			return -1;
237
+		}
238
+	}
239
+
240
+	/* add content-type */
241
+	if(msg->content_type == NULL
242
+			|| msg->content_type->body.len != content_type_sdp.len
243
+			|| strncmp(msg->content_type->body.s, content_type_sdp.s,
244
+					   content_type_sdp.len)
245
+					   != 0) {
246
+		if(msg->content_type != NULL) {
247
+			if(del_lump(msg, msg->content_type->name.s - msg->buf,
248
+					   msg->content_type->len, 0)
249
+					== 0) {
250
+				LM_ERR("failed to delete content type\n");
251
+				return -1;
252
+			}
253
+		}
254
+		value_len = content_type_sdp.len;
255
+		len = sizeof("Content-Type: ") - 1 + value_len + CRLF_LEN;
256
+		buf = pkg_malloc(sizeof(char) * (len));
257
+		if(!buf) {
258
+			LM_ERR("out of pkg memory\n");
259
+			return -1;
260
+		}
261
+		memcpy(buf, "Content-Type: ", sizeof("Content-Type: ") - 1);
262
+		memcpy(buf + sizeof("Content-Type: ") - 1, content_type_sdp.s,
263
+				value_len);
264
+		memcpy(buf + sizeof("Content-Type: ") - 1 + value_len, CRLF, CRLF_LEN);
265
+		if(insert_new_lump_after(anchor, buf, len, 0) == 0) {
266
+			LM_ERR("failed to insert content-type lump\n");
267
+			pkg_free(buf);
268
+			return -1;
269
+		}
270
+	}
271
+	anchor = anchor_lump(msg, body.s - msg->buf, 0, 0);
272
+
273
+	if(anchor == 0) {
274
+		LM_ERR("failed to get body anchor\n");
275
+		return -1;
276
+	}
277
+
278
+	buf = pkg_malloc(sizeof(char) * (new_body->len));
279
+	if(!buf) {
280
+		LM_ERR("out of pkg memory\n");
281
+		return -1;
282
+	}
283
+	memcpy(buf, new_body->s, new_body->len);
284
+	if(!insert_new_lump_after(anchor, buf, new_body->len, 0)) {
285
+		LM_ERR("failed to insert body lump\n");
286
+		pkg_free(buf);
287
+		return -1;
288
+	}
289
+	LM_DBG("new body: [%.*s]", new_body->len, new_body->s);
290
+	return 1;
291
+}
0 292
new file mode 100644
... ...
@@ -0,0 +1,45 @@
1
+/*
2
+ * Copyright (C) 2017-2018 Julien Chavanton jchavanton@gmail.com
3
+ *
4
+ * This file is part of Kamailio, a free SIP server.
5
+ *
6
+ * Kamailio is free software; you can redistribute it and/or modify
7
+ * it under the terms of the GNU General Public License as published by
8
+ * the Free Software Foundation; either version 2 of the License, or
9
+ * (at your option) any later version
10
+ *
11
+ * Kamailio is distributed in the hope that it will be useful,
12
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
13
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14
+ * GNU General Public License for more details.
15
+ *
16
+ * You should have received a copy of the GNU General Public License
17
+ * along with this program; if not, write to the Free Software
18
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301 USA
19
+ */
20
+
21
+#ifndef rms_sdp_h
22
+#define rms_sdp_h
23
+
24
+#include "../../core/sr_module.h"
25
+#include <mediastreamer2/mediastream.h>
26
+
27
+typedef struct rms_sdp_info
28
+{
29
+	str remote_ip;
30
+	str local_ip;
31
+	str payloads;
32
+	int remote_port;
33
+	int ipv6;
34
+	str new_body;
35
+	str recv_body;
36
+	int udp_local_port;
37
+} rms_sdp_info_t;
38
+
39
+int rms_sdp_set_body(struct sip_msg *msg, str *new_body);
40
+int rms_sdp_prepare_new_body(rms_sdp_info_t *, int payload_type_number);
41
+void rms_sdp_info_init(rms_sdp_info_t *sdp_info);
42
+void rms_sdp_info_free(rms_sdp_info_t *sdp_info);
43
+PayloadType *rms_sdp_check_payload(rms_sdp_info_t *);
44
+
45
+#endif
0 46
new file mode 100644
... ...
@@ -0,0 +1,73 @@
1
+/*
2
+ * Copyright (C) 2017-2018 Julien Chavanton jchavanton@gmail.com
3
+ *
4
+ * This file is part of Kamailio, a free SIP server.
5
+ *
6
+ * Kamailio is free software; you can redistribute it and/or modify
7
+ * it under the terms of the GNU General Public License as published by
8
+ * the Free Software Foundation; either version 2 of the License, or
9
+ * (at your option) any later version
10
+ *
11
+ * Kamailio is distributed in the hope that it will be useful,
12
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
13
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14
+ * GNU General Public License for more details.
15
+ *
16
+ * You should have received a copy of the GNU General Public License
17
+ * along with this program; if not, write to the Free Software
18
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
19
+ */
20
+
21
+#ifndef rms_util_h
22
+#define rms_util_h
23
+
24
+/**
25
+ * \brief Make a copy of a str structure using shm_malloc/pkg_malloc
26
+ * garanty to return a null terminated string, even if the source is not
27
+ * \param dst destination
28
+ * \param src source
29
+ * \param shared use shared memory
30
+ * \return 0 on success, -1 on failure
31
+ */
32
+static inline int rms_str_dup(str *dst, str *src, int shared)
33
+{
34
+	if(!dst) {
35
+		LM_ERR("dst null\n");
36
+		return -1;
37
+	}
38
+	dst->len = 0;
39
+	dst->s = NULL;
40
+	if(!src || !src->s || src->len < 0) {
41
+		LM_ERR("src null or invalid\n");
42
+		return 0;
43
+	}
44
+	if(src->len == 0)
45
+		return 1;
46
+	dst->len = src->len;
47
+	if(shared) {
48
+		dst->s = shm_malloc(dst->len+1);
49
+	} else {
50
+		dst->s = pkg_malloc(dst->len+1);
51
+	}
52
+	if(!dst->s) {
53
+		LM_ERR("%s_malloc: can't allocate memory (%d bytes)\n",
54
+				shared ? "shm" : "pkg", src->len);
55
+		return -1;
56
+	}
57
+	memcpy(dst->s, src->s, src->len);
58
+	dst->s[dst->len] = '\0';
59
+	return 1;
60
+}
61
+
62
+static inline char* rms_char_dup(char *s, int shared)
63
+{
64
+	str src;
65
+	str dst;
66
+	src.s = s;
67
+	src.len = strlen(s);
68
+	if(!rms_str_dup(&dst, &src, shared))
69
+		return NULL;
70
+	return dst.s;
71
+}
72
+
73
+#endif
0 74
new file mode 100644
... ...
@@ -0,0 +1,716 @@
1
+/*
2
+ * Copyright (C) 2017-2018 Julien Chavanton jchavanton@gmail.com
3
+ *
4
+ * This file is part of Kamailio, a free SIP server.
5
+ *
6
+ * Kamailio is free software; you can redistribute it and/or modify
7
+ * it under the terms of the GNU General Public License as published by
8
+ * the Free Software Foundation; either version 2 of the License, or
9
+ * (at your option) any later version
10
+ *
11
+ * Kamailio is distributed in the hope that it will be useful,
12
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
13
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14
+ * GNU General Public License for more details.
15
+ *
16
+ * You should have received a copy of the GNU General Public License
17
+ * along with this program; if not, write to the Free Software
18
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301 USA
19
+ */
20
+
21
+#include "rtp_media_server.h"
22
+#include "../../core/fmsg.h"
23
+
24
+MODULE_VERSION
25
+
26
+static int mod_init(void);
27
+static void mod_destroy(void);
28
+static int child_init(int);
29
+
30
+static rms_session_info_t *rms_session_list;
31
+str playback_fn = {0, 0};
32
+str log_fn = {0, 0};
33
+
34
+static rms_t rms;
35
+
36
+static int rms_session_free(rms_session_info_t *si);
37
+static rms_session_info_t *rms_session_search(char *callid, int len);
38
+static int fixup_rms_action_play(void **param, int param_no);
39
+static int rms_hangup_call(rms_session_info_t *si);
40
+
41
+static int rms_answer_f(struct sip_msg *);
42
+static int rms_action_play_f(struct sip_msg *, str *, str *);
43
+static int rms_sdp_offer_f(struct sip_msg *, char *, char *);
44
+static int rms_sdp_answer_f(struct sip_msg *, char *, char *);
45
+static int rms_media_stop_f(struct sip_msg *, char *, char *);
46
+static int rms_hangup_f(struct sip_msg *);
47
+static int rms_sessions_dump_f(struct sip_msg *, char *, char *);
48
+
49
+static cmd_export_t cmds[] = {
50
+		{"rms_answer", (cmd_function)rms_answer_f, 0, 0, 0, ANY_ROUTE},
51
+		{"rms_play", (cmd_function)rms_action_play_f, 2, fixup_rms_action_play, 0,
52
+				ANY_ROUTE},
53
+		{"rms_sdp_offer", (cmd_function)rms_sdp_offer_f, 0, 0, 0, ANY_ROUTE},
54
+		{"rms_sdp_answer", (cmd_function)rms_sdp_answer_f, 0, 0, 0, ANY_ROUTE},
55
+		{"rms_media_stop", (cmd_function)rms_media_stop_f, 0, 0, 0, ANY_ROUTE},
56
+		{"rms_hangup", (cmd_function)rms_hangup_f, 0, 0, 0, ANY_ROUTE},
57
+		{"rms_sessions_dump", (cmd_function)rms_sessions_dump_f, 0, 0, 0,
58
+				ANY_ROUTE},
59
+		{0, 0, 0, 0, 0, 0}};
60
+
61
+static param_export_t mod_params[] = {
62
+		{"log_file_name", PARAM_STR, &log_fn}, {0, 0, 0}};
63
+
64
+struct module_exports exports = {
65
+		"rtp_media_server", DEFAULT_DLFLAGS, /* dlopen flags */
66
+		cmds, mod_params, 0,				 /* RPC export */
67
+		0, 0, mod_init, child_init, mod_destroy,
68
+};
69
+
70
+static void run_action_route(rms_session_info_t *si, char *route)
71
+{
72
+	int rt, backup_rt;
73
+	struct run_act_ctx ctx;
74
+	sip_msg_t *fmsg;
75
+
76
+	if(route == NULL) {
77
+		LM_ERR("bad route\n");
78
+		return;
79
+	}
80
+	rt = -1;
81
+	rt = route_lookup(&event_rt, route);
82
+	if(rt < 0 || event_rt.rlist[rt] == NULL) {
83
+		LM_DBG("route does not exist");
84
+		return;
85
+	}
86
+	if(faked_msg_init() < 0) {
87
+		LM_ERR("faked_msg_init() failed\n");
88
+		return;
89
+	}
90
+	fmsg = faked_msg_next();
91
+	struct hdr_field callid;
92
+	callid.body.s = si->callid.s;
93
+	callid.body.len = si->callid.len;
94
+
95
+	fmsg->callid = &callid;
96
+
97
+	backup_rt = get_route_type();
98
+	set_route_type(EVENT_ROUTE);
99
+	init_run_actions_ctx(&ctx);
100
+	if(rt >= 0) {
101
+		run_top_route(event_rt.rlist[rt], fmsg, 0);
102
+	}
103
+	set_route_type(backup_rt);
104
+}
105
+
106
+static int fixup_rms_action_play(void **param, int param_no)
107
+{
108
+	if(param_no == 1)
109
+		return fixup_spve_null(param, 1);
110
+	if(param_no == 2)
111
+		return fixup_spve_null(param, 1);
112
+	LM_ERR("invalid parameter count [%d]\n", param_no);
113
+	return -1;
114
+}
115
+
116
+/**
117
+ * @return 0 to continue to load the OpenSER, -1 to stop the loading
118
+ * and abort OpenSER.
119
+ */
120
+static int mod_init(void)
121
+{
122
+	LM_INFO("RTP media server module init\n");
123
+	rms.udp_start_port = 50000;
124
+	rms.udp_end_port = 60000;
125
+	rms.udp_last_port = 50000;
126
+	rms_media_init();
127
+	rms_session_list = shm_malloc(sizeof(rms_session_info_t));
128
+	clist_init(rms_session_list, next, prev);
129
+
130
+	register_procs(1);
131
+	if(load_tm_api(&tmb) != 0) {
132
+		LM_ERR("can't load TM API\n");
133
+		return -1;
134
+	}
135
+	FILE *log_file = fopen(log_fn.s, "w+");
136
+	if(log_file) {
137
+		LM_INFO("ortp logs are redirected [%s]\n", log_fn.s);
138
+	} else {
139
+		log_file = stdout;
140
+		LM_INFO("ortp can not open logs file [%s]\n", log_fn.s);
141
+	}
142
+	ortp_set_log_file(log_file);
143
+	ortp_set_log_level_mask(
144
+			NULL, ORTP_MESSAGE | ORTP_WARNING | ORTP_ERROR | ORTP_FATAL);
145
+	return (0);
146
+}
147
+
148
+/**
149
+ * Called only once when OpenSER is shuting down to clean up module
150
+ * resources.
151
+ */
152
+static void mod_destroy()
153
+{
154
+	rms_media_destroy();
155
+	LM_INFO("RTP media server module destroy\n");
156
+	return;
157
+}
158
+
159
+void rms_signal_handler(int signum)
160
+{
161
+	LM_INFO("signal received [%d]\n", signum);
162
+}
163
+
164
+/**
165
+ * Most interaction with the session and media streams that are controlled 
166
+ * in this function this is safer in the event where a library is using non shared memory
167
+ * all the mediastreamer2 ticker threads are spawned from here.
168
+ */
169
+static void rms_session_manage_loop()
170
+{
171
+	while(1) {
172
+		lock(&session_list_mutex);
173
+		rms_session_info_t *si;
174
+		clist_foreach(rms_session_list, si, next)
175
+		{
176
+			if(si->action == RMS_HANGUP) {
177
+				LM_INFO("session action RMS_HANGUP [%s]\n", si->callid.s);
178
+				rms_hangup_call(si);
179
+				si->action = RMS_STOP;
180
+			} else if(si->action == RMS_STOP) {
181
+				LM_INFO("session action RMS_STOP [%s]\n", si->callid.s);
182
+				rms_stop_media(&si->caller_media);
183