├── .gitignore
├── Account.cs
├── Call.cs
├── LICENSE
├── Linphone.cs
├── Phone.cs
├── README.md
├── lib
└── linphone.zip
└── sipdotnet.csproj
/.gitignore:
--------------------------------------------------------------------------------
1 | ##############################################################################
2 | # General
3 | ##############################################################################
4 |
5 | # OS junk files
6 | [Tt]humbs.db
7 | *.DS_Store
8 |
9 | # Visual Studio / MonoDevelop
10 | *.[Oo]bj
11 | *.exe
12 | *.dll
13 | *.pdb
14 | *.user
15 | *.aps
16 | *.pch
17 | *.vspscc
18 | *.vssscc
19 | *_i.c
20 | *_p.c
21 | *.ncb
22 | *.suo
23 | *.tlb
24 | *.tlh
25 | *.bak
26 | *.ilk
27 | *.log
28 | *.lib
29 | *.sbr
30 | *.sdf
31 | *.opensdf
32 | *.resources
33 | *.res
34 | ipch/
35 | obj/
36 | [Bb]in
37 | [Dd]ebug*/
38 | [Rr]elease*/
39 | Ankh.NoLoad
40 | *.gpState
41 | .vscode/
42 | *.exp
43 |
44 | # Tooling
45 | _ReSharper*/
46 | *.resharper
47 | [Tt]est[Rr]esult*
48 | *.orig
49 | *.rej
50 |
51 | # NuGet packages
52 | !.nuget/*
53 | [Pp]ackages/*
54 | ![Pp]ackages/repositories.config
55 |
56 | # Temporary Files
57 | ~.*
58 | ~$*
59 |
60 | # Autotools-generated files
61 | /Makefile
62 | Makefile.in
63 | aclocal.m4
64 | autom4te.cache
65 | /build/
66 | config.cache
67 | config.guess
68 | config.h
69 | config.h.in
70 | config.log
71 | config.status
72 | config.sub
73 | configure
74 | configure.scan
75 | cygconfig.h
76 | depcomp
77 | install-sh
78 | libtool
79 | ltmain.sh
80 | missing
81 | mkinstalldirs
82 | releases
83 | stamp-h
84 | stamp-h1
85 | stamp-h.in
86 | /test-driver
87 | *~
88 | *.swp
89 | *.o
90 | .deps
91 |
92 | # Libtool
93 | libtool.m4
94 | lt~obsolete.m4
95 | ltoptions.m4
96 | ltsugar.m4
97 | ltversion.m4
98 |
99 | # Dolt (libtool replacement)
100 | doltlibtool
101 | doltcompile
102 |
103 | # pkg-config
104 | *.pc
105 |
106 | # Emacs
107 | semantic.cache
108 |
109 | # gtags
110 | GPATH
111 | GRTAGS
112 | GSYMS
113 | GTAGS
114 |
115 | # Doxygen
116 | docs/doxygen*
117 | docs/perlmod*
118 |
119 |
120 | ##############################################################################
121 | # Mono-specific patterns
122 | ##############################################################################
123 |
124 | .dirstamp
125 | compile
126 | mono.h
127 | mono-*.tar.*
128 | tmpinst-dir.stamp
129 | msvc/scripts/inputs/
130 | extensions-config.h
131 |
132 |
--------------------------------------------------------------------------------
/Account.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Diagnostics;
3 |
4 | namespace sipdotnet
5 | {
6 | public class Account
7 | {
8 | string username, password, server;
9 | int port = 5060, id;
10 |
11 | public string Username {
12 | get {
13 | return username;
14 | }
15 | set {
16 | username = value;
17 | }
18 | }
19 |
20 | public string Password {
21 | get {
22 | return password;
23 | }
24 | set {
25 | password = value;
26 | }
27 | }
28 |
29 | public string Server {
30 | get {
31 | return server;
32 | }
33 | set {
34 | server = value;
35 | }
36 | }
37 |
38 | public int Id {
39 | get {
40 | return id;
41 | }
42 | set {
43 | id = value;
44 | }
45 | }
46 |
47 | public int Port {
48 | get {
49 | return port;
50 | }
51 | set {
52 | port = value;
53 | }
54 | }
55 |
56 | public string Identity
57 | {
58 | get { return "sip:" + this.username + "@" + this.server; }
59 | }
60 |
61 | public Account(string username, string password, string server, int port)
62 | {
63 | Debug.Assert(!String.IsNullOrEmpty(username), "User cannot be empty.");
64 | Debug.Assert(!String.IsNullOrEmpty(server), "Server cannot be empty.");
65 |
66 | this.username = username;
67 | this.password = password;
68 | this.server = server;
69 | this.port = port;
70 | }
71 |
72 | public Account(string username, string password, string server) : this(username, password, server, 5060) { }
73 | }
74 | }
75 |
76 |
--------------------------------------------------------------------------------
/Call.cs:
--------------------------------------------------------------------------------
1 | using System;
2 |
3 | namespace sipdotnet
4 | {
5 | public class Call
6 | {
7 | public enum CallType
8 | {
9 | None,
10 | Incoming,
11 | Outcoming
12 | };
13 |
14 | protected CallType calltype = CallType.None;
15 |
16 | public CallType GetCallType ()
17 | {
18 | return this.calltype;
19 | }
20 |
21 | public enum CallState
22 | {
23 | None,
24 | Loading,
25 | Active,
26 | Completed,
27 | Error
28 | };
29 |
30 | protected CallState callstate = CallState.None;
31 |
32 | public CallState GetState ()
33 | {
34 | return this.callstate;
35 | }
36 |
37 | protected string from;
38 |
39 | public string GetFrom ()
40 | {
41 | return this.from;
42 | }
43 |
44 | protected string to;
45 |
46 | public string GetTo ()
47 | {
48 | return this.to;
49 | }
50 |
51 | }
52 | }
53 |
54 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | GNU LESSER GENERAL PUBLIC LICENSE
2 | Version 3, 29 June 2007
3 |
4 | Copyright (C) 2007 Free Software Foundation, Inc.
5 | Everyone is permitted to copy and distribute verbatim copies
6 | of this license document, but changing it is not allowed.
7 |
8 |
9 | This version of the GNU Lesser General Public License incorporates
10 | the terms and conditions of version 3 of the GNU General Public
11 | License, supplemented by the additional permissions listed below.
12 |
13 | 0. Additional Definitions.
14 |
15 | As used herein, "this License" refers to version 3 of the GNU Lesser
16 | General Public License, and the "GNU GPL" refers to version 3 of the GNU
17 | General Public License.
18 |
19 | "The Library" refers to a covered work governed by this License,
20 | other than an Application or a Combined Work as defined below.
21 |
22 | An "Application" is any work that makes use of an interface provided
23 | by the Library, but which is not otherwise based on the Library.
24 | Defining a subclass of a class defined by the Library is deemed a mode
25 | of using an interface provided by the Library.
26 |
27 | A "Combined Work" is a work produced by combining or linking an
28 | Application with the Library. The particular version of the Library
29 | with which the Combined Work was made is also called the "Linked
30 | Version".
31 |
32 | The "Minimal Corresponding Source" for a Combined Work means the
33 | Corresponding Source for the Combined Work, excluding any source code
34 | for portions of the Combined Work that, considered in isolation, are
35 | based on the Application, and not on the Linked Version.
36 |
37 | The "Corresponding Application Code" for a Combined Work means the
38 | object code and/or source code for the Application, including any data
39 | and utility programs needed for reproducing the Combined Work from the
40 | Application, but excluding the System Libraries of the Combined Work.
41 |
42 | 1. Exception to Section 3 of the GNU GPL.
43 |
44 | You may convey a covered work under sections 3 and 4 of this License
45 | without being bound by section 3 of the GNU GPL.
46 |
47 | 2. Conveying Modified Versions.
48 |
49 | If you modify a copy of the Library, and, in your modifications, a
50 | facility refers to a function or data to be supplied by an Application
51 | that uses the facility (other than as an argument passed when the
52 | facility is invoked), then you may convey a copy of the modified
53 | version:
54 |
55 | a) under this License, provided that you make a good faith effort to
56 | ensure that, in the event an Application does not supply the
57 | function or data, the facility still operates, and performs
58 | whatever part of its purpose remains meaningful, or
59 |
60 | b) under the GNU GPL, with none of the additional permissions of
61 | this License applicable to that copy.
62 |
63 | 3. Object Code Incorporating Material from Library Header Files.
64 |
65 | The object code form of an Application may incorporate material from
66 | a header file that is part of the Library. You may convey such object
67 | code under terms of your choice, provided that, if the incorporated
68 | material is not limited to numerical parameters, data structure
69 | layouts and accessors, or small macros, inline functions and templates
70 | (ten or fewer lines in length), you do both of the following:
71 |
72 | a) Give prominent notice with each copy of the object code that the
73 | Library is used in it and that the Library and its use are
74 | covered by this License.
75 |
76 | b) Accompany the object code with a copy of the GNU GPL and this license
77 | document.
78 |
79 | 4. Combined Works.
80 |
81 | You may convey a Combined Work under terms of your choice that,
82 | taken together, effectively do not restrict modification of the
83 | portions of the Library contained in the Combined Work and reverse
84 | engineering for debugging such modifications, if you also do each of
85 | the following:
86 |
87 | a) Give prominent notice with each copy of the Combined Work that
88 | the Library is used in it and that the Library and its use are
89 | covered by this License.
90 |
91 | b) Accompany the Combined Work with a copy of the GNU GPL and this license
92 | document.
93 |
94 | c) For a Combined Work that displays copyright notices during
95 | execution, include the copyright notice for the Library among
96 | these notices, as well as a reference directing the user to the
97 | copies of the GNU GPL and this license document.
98 |
99 | d) Do one of the following:
100 |
101 | 0) Convey the Minimal Corresponding Source under the terms of this
102 | License, and the Corresponding Application Code in a form
103 | suitable for, and under terms that permit, the user to
104 | recombine or relink the Application with a modified version of
105 | the Linked Version to produce a modified Combined Work, in the
106 | manner specified by section 6 of the GNU GPL for conveying
107 | Corresponding Source.
108 |
109 | 1) Use a suitable shared library mechanism for linking with the
110 | Library. A suitable mechanism is one that (a) uses at run time
111 | a copy of the Library already present on the user's computer
112 | system, and (b) will operate properly with a modified version
113 | of the Library that is interface-compatible with the Linked
114 | Version.
115 |
116 | e) Provide Installation Information, but only if you would otherwise
117 | be required to provide such information under section 6 of the
118 | GNU GPL, and only to the extent that such information is
119 | necessary to install and execute a modified version of the
120 | Combined Work produced by recombining or relinking the
121 | Application with a modified version of the Linked Version. (If
122 | you use option 4d0, the Installation Information must accompany
123 | the Minimal Corresponding Source and Corresponding Application
124 | Code. If you use option 4d1, you must provide the Installation
125 | Information in the manner specified by section 6 of the GNU GPL
126 | for conveying Corresponding Source.)
127 |
128 | 5. Combined Libraries.
129 |
130 | You may place library facilities that are a work based on the
131 | Library side by side in a single library together with other library
132 | facilities that are not Applications and are not covered by this
133 | License, and convey such a combined library under terms of your
134 | choice, if you do both of the following:
135 |
136 | a) Accompany the combined library with a copy of the same work based
137 | on the Library, uncombined with any other library facilities,
138 | conveyed under the terms of this License.
139 |
140 | b) Give prominent notice with the combined library that part of it
141 | is a work based on the Library, and explaining where to find the
142 | accompanying uncombined form of the same work.
143 |
144 | 6. Revised Versions of the GNU Lesser General Public License.
145 |
146 | The Free Software Foundation may publish revised and/or new versions
147 | of the GNU Lesser General Public License from time to time. Such new
148 | versions will be similar in spirit to the present version, but may
149 | differ in detail to address new problems or concerns.
150 |
151 | Each version is given a distinguishing version number. If the
152 | Library as you received it specifies that a certain numbered version
153 | of the GNU Lesser General Public License "or any later version"
154 | applies to it, you have the option of following the terms and
155 | conditions either of that published version or of any later version
156 | published by the Free Software Foundation. If the Library as you
157 | received it does not specify a version number of the GNU Lesser
158 | General Public License, you may choose any version of the GNU Lesser
159 | General Public License ever published by the Free Software Foundation.
160 |
161 | If the Library as you received it specifies that a proxy can decide
162 | whether future versions of the GNU Lesser General Public License shall
163 | apply, that proxy's public statement of acceptance of any version is
164 | permanent authorization for you to choose that version for the
165 | Library.
--------------------------------------------------------------------------------
/Linphone.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Runtime.InteropServices;
3 | using System.Threading;
4 | using System.Collections.Generic;
5 |
6 | namespace sipdotnet
7 | {
8 | public class Linphone
9 | {
10 |
11 | #if (WINDOWS)
12 | const string LIBNAME = "linphone.dll";
13 | #else
14 | const string LIBNAME = "liblinphone";
15 | #endif
16 |
17 | #region Import
18 |
19 | ///
20 | /// Disable a sip transport
21 | ///
22 | const int LC_SIP_TRANSPORT_DISABLED = 0;
23 |
24 | ///
25 | /// Randomly chose a sip port for this transport
26 | ///
27 | const int LC_SIP_TRANSPORT_RANDOM = -1;
28 |
29 | ///
30 | /// Don't create any server socket for this transport, ie don't bind on any port
31 | ///
32 | const int LC_SIP_TRANSPORT_DONTBIND = -2;
33 |
34 | ///
35 | /// Linphone core SIP transport ports
36 | /// http://www.linphone.org/docs/liblinphone/struct__LinphoneSipTransports.html
37 | ///
38 | struct LCSipTransports
39 | {
40 | ///
41 | /// UDP port to listening on, negative value if not set
42 | ///
43 | public int udp_port;
44 |
45 | ///
46 | /// TCP port to listening on, negative value if not set
47 | ///
48 | public int tcp_port;
49 |
50 | ///
51 | /// DTLS port to listening on, negative value if not set
52 | ///
53 | public int dtls_port;
54 |
55 | ///
56 | /// TLS port to listening on, negative value if not set
57 | ///
58 | public int tls_port;
59 | };
60 |
61 | ///
62 | /// Describes proxy registration states
63 | /// http://www.linphone.org/docs/liblinphone/group__proxies.html
64 | ///
65 | public enum LinphoneRegistrationState
66 | {
67 | ///
68 | /// Initial state for registrations
69 | ///
70 | LinphoneRegistrationNone,
71 |
72 | ///
73 | /// Registration is in progress
74 | ///
75 | LinphoneRegistrationProgress,
76 |
77 | ///
78 | /// Registration is successful
79 | ///
80 | LinphoneRegistrationOk,
81 |
82 | ///
83 | /// Unregistration succeeded
84 | ///
85 | LinphoneRegistrationCleared,
86 |
87 | ///
88 | /// Registration failed
89 | ///
90 | LinphoneRegistrationFailed
91 | };
92 |
93 | ///
94 | /// Logging level
95 | /// https://github.com/BelledonneCommunications/ortp/blob/master/include/ortp/logging.h
96 | /// https://github.com/BelledonneCommunications/bctoolbox/blob/master/include/bctoolbox/logging.h
97 | ///
98 | public enum OrtpLogLevel
99 | {
100 | DEBUG = 1,
101 | TRACE = 1 << 1,
102 | MESSAGE = 1 << 2,
103 | WARNING = 1 << 3,
104 | ERROR = 1 << 4,
105 | FATAL = 1 << 5,
106 | END = 1 << 6
107 | };
108 |
109 | ///
110 | /// Represents the different state a call can reach into
111 | /// http://www.linphone.org/docs/liblinphone/group__call__control.html
112 | ///
113 | public enum LinphoneCallState
114 | {
115 | ///
116 | /// Initial call state
117 | ///
118 | LinphoneCallIdle,
119 |
120 | ///
121 | /// This is a new incoming call
122 | ///
123 | LinphoneCallIncomingReceived,
124 |
125 | ///
126 | /// An outgoing call is started
127 | ///
128 | LinphoneCallOutgoingInit,
129 |
130 | ///
131 | /// An outgoing call is in progress
132 | ///
133 | LinphoneCallOutgoingProgress,
134 |
135 | ///
136 | /// An outgoing call is ringing at remote end
137 | ///
138 | LinphoneCallOutgoingRinging,
139 |
140 | ///
141 | /// An outgoing call is proposed early media
142 | ///
143 | LinphoneCallOutgoingEarlyMedia,
144 |
145 | ///
146 | /// Connected, the call is answered
147 | ///
148 | LinphoneCallConnected,
149 |
150 | ///
151 | /// The media streams are established and running
152 | ///
153 | LinphoneCallStreamsRunning,
154 |
155 | ///
156 | /// The call is pausing at the initiative of local end
157 | ///
158 | LinphoneCallPausing,
159 |
160 | ///
161 | /// The call is paused, remote end has accepted the pause
162 | ///
163 | LinphoneCallPaused,
164 |
165 | ///
166 | /// The call is being resumed by local end
167 | ///
168 | LinphoneCallResuming,
169 |
170 | ///
171 | ///
173 | LinphoneCallRefered,
174 |
175 | ///
176 | /// The call encountered an error
177 | ///
178 | LinphoneCallError,
179 |
180 | ///
181 | /// The call ended normally
182 | ///
183 | LinphoneCallEnd,
184 |
185 | ///
186 | /// The call is paused by remote end
187 | ///
188 | LinphoneCallPausedByRemote,
189 |
190 | ///
191 | /// The call's parameters change is requested by remote end, used for example when video is added by remote
192 | ///
193 | LinphoneCallUpdatedByRemote,
194 |
195 | ///
196 | /// We are proposing early media to an incoming call
197 | ///
198 | LinphoneCallIncomingEarlyMedia,
199 |
200 | ///
201 | /// A call update has been initiated by us
202 | ///
203 | LinphoneCallUpdating,
204 |
205 | ///
206 | /// The call object is no more retained by the core
207 | ///
208 | LinphoneCallReleased
209 | };
210 |
211 | ///
212 | /// Holds all callbacks that the application should implement. None is mandatory.
213 | /// http://www.linphone.org/docs/liblinphone/struct__LinphoneCoreVTable.html
214 | ///
215 | struct LinphoneCoreVTable
216 | {
217 | ///
218 | /// Notifies global state changes
219 | ///
220 | public IntPtr global_state_changed;
221 |
222 | ///
223 | /// Notifies registration state changes
224 | ///
225 | public IntPtr registration_state_changed;
226 |
227 | ///
228 | /// Notifies call state changes
229 | ///
230 | public IntPtr call_state_changed;
231 |
232 | ///
233 | /// Notify received presence events
234 | ///
235 | public IntPtr notify_presence_received;
236 |
237 | ///
238 | /// Notify received presence events
239 | ///
240 | public IntPtr notify_presence_received_for_uri_or_tel;
241 |
242 | ///
243 | /// Notify about pending presence subscription request
244 | ///
245 | public IntPtr new_subscription_requested;
246 |
247 | ///
248 | /// Ask the application some authentication information
249 | ///
250 | public IntPtr auth_info_requested;
251 |
252 | ///
253 | /// Ask the application some authentication information
254 | ///
255 | public IntPtr authentication_requested;
256 |
257 | ///
258 | /// Notifies that call log list has been updated
259 | ///
260 | public IntPtr call_log_updated;
261 |
262 | ///
263 | /// A message is received, can be text or external body
264 | ///
265 | public IntPtr message_received;
266 |
267 | ///
268 | /// An encrypted message is received but we can't decrypt it
269 | ///
270 | public IntPtr message_received_unable_decrypt;
271 |
272 | ///
273 | /// An is-composing notification has been received
274 | ///
275 | public IntPtr is_composing_received;
276 |
277 | ///
278 | /// A dtmf has been received received
279 | ///
280 | public IntPtr dtmf_received;
281 |
282 | ///
283 | /// An out of call refer was received
284 | ///
285 | public IntPtr refer_received;
286 |
287 | ///
288 | /// Notifies on change in the encryption of call streams
289 | ///
290 | public IntPtr call_encryption_changed;
291 |
292 | ///
293 | /// Notifies when a transfer is in progress
294 | ///
295 | public IntPtr transfer_state_changed;
296 |
297 | ///
298 | /// A LinphoneFriend's BuddyInfo has changed
299 | ///
300 | public IntPtr buddy_info_updated;
301 |
302 | ///
303 | /// Notifies on refreshing of call's statistics.
304 | ///
305 | public IntPtr call_stats_updated;
306 |
307 | ///
308 | /// Notifies an incoming informational message received.
309 | ///
310 | public IntPtr info_received;
311 |
312 | ///
313 | /// Notifies subscription state change
314 | ///
315 | public IntPtr subscription_state_changed;
316 |
317 | ///
318 | /// Notifies a an event notification, see linphone_core_subscribe()
319 | ///
320 | public IntPtr notify_received;
321 |
322 | ///
323 | /// Notifies publish state change (only from #LinphoneEvent api)
324 | ///
325 | public IntPtr publish_state_changed;
326 |
327 | ///
328 | /// Notifies configuring status changes
329 | ///
330 | public IntPtr configuring_status;
331 |
332 | ///
333 | /// Callback that notifies various events with human readable text (deprecated)
334 | ///
335 | [System.Obsolete]
336 | public IntPtr display_status;
337 |
338 | ///
339 | /// Callback to display a message to the user (deprecated)
340 | ///
341 | [System.Obsolete]
342 | public IntPtr display_message;
343 |
344 | ///
345 | /// Callback to display a warning to the user (deprecated)
346 | ///
347 | [System.Obsolete]
348 | public IntPtr display_warning;
349 |
350 | [System.Obsolete]
351 | public IntPtr display_url;
352 |
353 | ///
354 | /// Notifies the application that it should show up
355 | ///
356 | [System.Obsolete]
357 | public IntPtr show;
358 |
359 | ///
360 | /// Use #message_received instead
A text message has been received
361 | ///
362 | [System.Obsolete]
363 | public IntPtr text_received;
364 |
365 | ///
366 | /// Callback to store file received attached to a LinphoneChatMessage
367 | ///
368 | [System.Obsolete]
369 | public IntPtr file_transfer_recv;
370 |
371 | ///
372 | /// Callback to collect file chunk to be sent for a LinphoneChatMessage
373 | ///
374 | [System.Obsolete]
375 | public IntPtr file_transfer_send;
376 |
377 | ///
378 | /// Callback to indicate file transfer progress
379 | ///
380 | [System.Obsolete]
381 | public IntPtr file_transfer_progress_indication;
382 |
383 | ///
384 | /// Callback to report IP network status (I.E up/down)
385 | ///
386 | public IntPtr network_reachable;
387 |
388 | ///
389 | /// Callback to upload collected logs
390 | ///
391 | public IntPtr log_collection_upload_state_changed;
392 |
393 | ///
394 | /// Callback to indicate log collection upload progress
395 | ///
396 | public IntPtr log_collection_upload_progress_indication;
397 |
398 | public IntPtr friend_list_created;
399 |
400 | public IntPtr friend_list_removed;
401 |
402 | ///
403 | /// User data associated with the above callbacks
404 | ///
405 | public IntPtr user_data;
406 | };
407 |
408 | #region Initializing
409 |
410 | // http://www.linphone.org/docs/liblinphone/group__initializing.html
411 |
412 | [DllImport(LIBNAME, CallingConvention = CallingConvention.Cdecl)]
413 | [System.Obsolete]
414 | static extern void linphone_core_enable_logs (IntPtr FILE);
415 |
416 | [DllImport(LIBNAME, CallingConvention = CallingConvention.Cdecl)]
417 | [System.Obsolete]
418 | static extern void linphone_core_disable_logs ();
419 |
420 | [DllImport(LIBNAME, CallingConvention = CallingConvention.Cdecl)]
421 | [System.Obsolete]
422 | static extern IntPtr linphone_core_new (IntPtr vtable, string config_path, string factory_config_path, IntPtr userdata);
423 |
424 | [DllImport(LIBNAME, CallingConvention = CallingConvention.Cdecl)]
425 | static extern void linphone_core_unref (IntPtr lc);
426 |
427 | [DllImport(LIBNAME, CallingConvention = CallingConvention.Cdecl)]
428 | static extern void linphone_core_iterate (IntPtr lc);
429 |
430 | [DllImport(LIBNAME, CallingConvention = CallingConvention.Cdecl)]
431 | static extern void linphone_core_set_log_level (OrtpLogLevel loglevel);
432 |
433 | #endregion
434 |
435 | #region Proxies
436 |
437 | // http://www.linphone.org/docs/liblinphone/group__proxies.html
438 |
439 | [DllImport(LIBNAME, CallingConvention = CallingConvention.Cdecl)]
440 | static extern IntPtr linphone_core_create_proxy_config (IntPtr lc);
441 |
442 | [DllImport(LIBNAME, CallingConvention = CallingConvention.Cdecl)]
443 | static extern int linphone_proxy_config_set_identity (IntPtr obj, string identity);
444 |
445 | [DllImport(LIBNAME, CallingConvention = CallingConvention.Cdecl)]
446 | static extern int linphone_proxy_config_set_server_addr (IntPtr obj, string server_addr);
447 |
448 | [DllImport(LIBNAME, CallingConvention = CallingConvention.Cdecl)]
449 | static extern void linphone_proxy_config_enable_register (IntPtr obj, bool val);
450 |
451 | [DllImport(LIBNAME, CallingConvention = CallingConvention.Cdecl)]
452 | static extern int linphone_core_add_proxy_config (IntPtr lc, IntPtr cfg);
453 |
454 | [DllImport(LIBNAME, CallingConvention = CallingConvention.Cdecl)]
455 | static extern void linphone_core_set_default_proxy_config (IntPtr lc, IntPtr config);
456 |
457 | [DllImport(LIBNAME, CallingConvention = CallingConvention.Cdecl)]
458 | [System.Obsolete]
459 | static extern int linphone_core_get_default_proxy (IntPtr lc, ref IntPtr config);
460 |
461 | [DllImport(LIBNAME, CallingConvention = CallingConvention.Cdecl)]
462 | static extern bool linphone_proxy_config_is_registered (IntPtr config);
463 |
464 | [DllImport(LIBNAME, CallingConvention = CallingConvention.Cdecl)]
465 | static extern void linphone_proxy_config_edit (IntPtr config);
466 |
467 | [DllImport(LIBNAME, CallingConvention = CallingConvention.Cdecl)]
468 | static extern int linphone_proxy_config_done (IntPtr config);
469 |
470 | #endregion
471 |
472 | #region Network
473 |
474 | // http://www.linphone.org/docs/liblinphone/group__network__parameters.html
475 |
476 | [DllImport(LIBNAME, CallingConvention = CallingConvention.Cdecl)]
477 | static extern int linphone_core_set_sip_transports (IntPtr lc, IntPtr tr_config);
478 |
479 | #endregion
480 |
481 | #region SIP
482 |
483 | // http://www.linphone.org/docs/liblinphone/group__linphone__address.html
484 |
485 | [DllImport(LIBNAME, CallingConvention = CallingConvention.Cdecl)]
486 | [System.Obsolete]
487 | static extern void linphone_address_destroy (IntPtr u);
488 |
489 | #endregion
490 |
491 | #region Miscenalleous
492 |
493 | [DllImport(LIBNAME, CallingConvention = CallingConvention.Cdecl)]
494 | static extern void linphone_core_set_user_agent (IntPtr lc, string ua_name, string version);
495 |
496 | #endregion
497 |
498 | #region Calls
499 |
500 | // http://www.linphone.org/docs/liblinphone/group__call__control.html
501 |
502 | [DllImport(LIBNAME, CallingConvention = CallingConvention.Cdecl)]
503 | static extern IntPtr linphone_core_create_call_params (IntPtr lc, IntPtr call);
504 |
505 | [DllImport(LIBNAME, CallingConvention = CallingConvention.Cdecl)]
506 | static extern void linphone_call_params_enable_video (IntPtr lc, bool enabled);
507 |
508 | [DllImport(LIBNAME, CallingConvention = CallingConvention.Cdecl)]
509 | static extern void linphone_call_params_enable_early_media_sending (IntPtr lc, bool enabled);
510 |
511 | [DllImport(LIBNAME, CallingConvention = CallingConvention.Cdecl)]
512 | static extern IntPtr linphone_core_invite_with_params (IntPtr lc, string url, IntPtr callparams);
513 |
514 | [DllImport(LIBNAME, CallingConvention = CallingConvention.Cdecl)]
515 | static extern void linphone_call_params_unref (IntPtr callparams);
516 |
517 | [DllImport(LIBNAME, CallingConvention = CallingConvention.Cdecl)]
518 | static extern int linphone_core_terminate_call (IntPtr lc, IntPtr call);
519 |
520 | [DllImport(LIBNAME, CallingConvention = CallingConvention.Cdecl)]
521 | static extern IntPtr linphone_call_ref(IntPtr call);
522 |
523 | [DllImport(LIBNAME, CallingConvention = CallingConvention.Cdecl)]
524 | static extern void linphone_call_unref (IntPtr call);
525 |
526 | [DllImport(LIBNAME, CallingConvention = CallingConvention.Cdecl)]
527 | static extern int linphone_core_terminate_all_calls (IntPtr lc);
528 |
529 | [DllImport(LIBNAME, CallingConvention = CallingConvention.Cdecl)]
530 | static extern IntPtr linphone_call_get_remote_address_as_string (IntPtr call);
531 |
532 | [DllImport(LIBNAME, CallingConvention = CallingConvention.Cdecl)]
533 | static extern int linphone_core_accept_call_with_params (IntPtr lc, IntPtr call, IntPtr callparams);
534 |
535 | [DllImport(LIBNAME, CallingConvention = CallingConvention.Cdecl)]
536 | static extern void linphone_call_params_set_record_file (IntPtr callparams, string filename);
537 |
538 | [DllImport(LIBNAME, CallingConvention = CallingConvention.Cdecl)]
539 | static extern IntPtr linphone_call_params_get_record_file (IntPtr callparams);
540 |
541 | [DllImport(LIBNAME, CallingConvention = CallingConvention.Cdecl)]
542 | static extern void linphone_call_params_enable_audio (IntPtr callparams, bool enabled);
543 |
544 | [DllImport(LIBNAME, CallingConvention = CallingConvention.Cdecl)]
545 | static extern int linphone_call_send_dtmfs (IntPtr call, string dtmfs);
546 |
547 | #endregion
548 |
549 | #region Authentication
550 |
551 | // http://www.linphone.org/docs/liblinphone/group__authentication.html
552 |
553 | [DllImport(LIBNAME, CallingConvention = CallingConvention.Cdecl)]
554 | static extern void linphone_core_add_auth_info (IntPtr lc, IntPtr info);
555 |
556 | [DllImport(LIBNAME, CallingConvention = CallingConvention.Cdecl)]
557 | static extern IntPtr linphone_auth_info_new (string username, string userid, string passwd, string ha1, string realm, string domain);
558 |
559 | #endregion
560 |
561 | #region Calls miscenalleous
562 |
563 | // http://www.linphone.org/docs/liblinphone/group__call__misc.html
564 |
565 | [DllImport(LIBNAME, CallingConvention = CallingConvention.Cdecl)]
566 | static extern void linphone_call_start_recording (IntPtr call);
567 |
568 | [DllImport(LIBNAME, CallingConvention = CallingConvention.Cdecl)]
569 | static extern void linphone_call_stop_recording (IntPtr call);
570 |
571 | #endregion
572 |
573 | #region Media
574 |
575 | // http://www.linphone.org/docs/liblinphone/group__media__parameters.html
576 |
577 | [DllImport(LIBNAME, CallingConvention = CallingConvention.Cdecl)]
578 | static extern void linphone_core_set_play_file (IntPtr lc, string file);
579 |
580 | [DllImport(LIBNAME, CallingConvention = CallingConvention.Cdecl)]
581 | static extern void linphone_core_set_record_file (IntPtr lc, string file);
582 |
583 | [DllImport(LIBNAME, CallingConvention = CallingConvention.Cdecl)]
584 | static extern void linphone_core_set_ring (IntPtr lc, string file);
585 |
586 | [DllImport(LIBNAME, CallingConvention = CallingConvention.Cdecl)]
587 | static extern void linphone_core_set_remote_ringback_tone (IntPtr lc, string file);
588 |
589 | [DllImport(LIBNAME, CallingConvention = CallingConvention.Cdecl)]
590 | static extern void linphone_core_set_ringback (IntPtr lc, string file);
591 |
592 | #endregion
593 |
594 | #endregion
595 |
596 | class LinphoneCall : Call
597 | {
598 | IntPtr linphoneCallPtr;
599 |
600 | public IntPtr LinphoneCallPtr {
601 | get {
602 | return linphoneCallPtr;
603 | }
604 | set {
605 | linphoneCallPtr = value;
606 | }
607 | }
608 |
609 | public void SetCallType (CallType type)
610 | {
611 | this.calltype = type;
612 | }
613 |
614 | public void SetCallState (CallState state)
615 | {
616 | this.callstate = state;
617 | }
618 |
619 | public void SetFrom (string from)
620 | {
621 | this.from = from;
622 | }
623 |
624 | public void SetTo (string to)
625 | {
626 | this.to = to;
627 | }
628 | }
629 |
630 | [UnmanagedFunctionPointer(CallingConvention.Cdecl)]
631 | delegate void LinphoneCoreRegistrationStateChangedCb (IntPtr lc, IntPtr cfg, LinphoneRegistrationState cstate, string message);
632 |
633 | [UnmanagedFunctionPointer(CallingConvention.Cdecl)]
634 | delegate void LinphoneCoreCallStateChangedCb (IntPtr lc, IntPtr call, LinphoneCallState cstate, string message);
635 |
636 | LinphoneCoreRegistrationStateChangedCb registration_state_changed;
637 | LinphoneCoreCallStateChangedCb call_state_changed;
638 | IntPtr linphoneCore, callsDefaultParams, proxy_cfg, auth_info, t_configPtr, vtablePtr;
639 | Thread coreLoop;
640 | bool running = true;
641 | string identity, server_addr;
642 | LinphoneCoreVTable vtable;
643 | LCSipTransports t_config;
644 |
645 | List calls = new List ();
646 |
647 | LinphoneCall FindCall (IntPtr call)
648 | {
649 | return calls.Find (delegate(LinphoneCall obj) {
650 | return (obj.LinphoneCallPtr == call);
651 | });
652 | }
653 |
654 | void SetTimeout (Action callback, int miliseconds)
655 | {
656 | System.Timers.Timer timeout = new System.Timers.Timer ();
657 | timeout.Interval = miliseconds;
658 | timeout.AutoReset = false;
659 | timeout.Elapsed += (object sender, System.Timers.ElapsedEventArgs e) => {
660 | callback ();
661 | };
662 | timeout.Start ();
663 | }
664 |
665 | public void CreatePhone (string username, string password, string server, int port, string agent, string version)
666 | {
667 | #if (TRACE)
668 | linphone_core_enable_logs (IntPtr.Zero);
669 | linphone_core_set_log_level (OrtpLogLevel.DEBUG);
670 | #else
671 | linphone_core_disable_logs ();
672 | linphone_core_set_log_level (OrtpLogLevel.END);
673 | #endif
674 |
675 | running = true;
676 | registration_state_changed = new LinphoneCoreRegistrationStateChangedCb(OnRegistrationChanged);
677 | call_state_changed = new LinphoneCoreCallStateChangedCb(OnCallStateChanged);
678 |
679 | #pragma warning disable 0612
680 | vtable = new LinphoneCoreVTable()
681 | {
682 | global_state_changed = IntPtr.Zero,
683 | registration_state_changed = Marshal.GetFunctionPointerForDelegate(registration_state_changed),
684 | call_state_changed = Marshal.GetFunctionPointerForDelegate(call_state_changed),
685 | notify_presence_received = IntPtr.Zero,
686 | notify_presence_received_for_uri_or_tel = IntPtr.Zero,
687 | new_subscription_requested = IntPtr.Zero,
688 | auth_info_requested = IntPtr.Zero,
689 | authentication_requested = IntPtr.Zero,
690 | call_log_updated = IntPtr.Zero,
691 | message_received = IntPtr.Zero,
692 | message_received_unable_decrypt = IntPtr.Zero,
693 | is_composing_received = IntPtr.Zero,
694 | dtmf_received = IntPtr.Zero,
695 | refer_received = IntPtr.Zero,
696 | call_encryption_changed = IntPtr.Zero,
697 | transfer_state_changed = IntPtr.Zero,
698 | buddy_info_updated = IntPtr.Zero,
699 | call_stats_updated = IntPtr.Zero,
700 | info_received = IntPtr.Zero,
701 | subscription_state_changed = IntPtr.Zero,
702 | notify_received = IntPtr.Zero,
703 | publish_state_changed = IntPtr.Zero,
704 | configuring_status = IntPtr.Zero,
705 | display_status = IntPtr.Zero,
706 | display_message = IntPtr.Zero,
707 | display_warning = IntPtr.Zero,
708 | display_url = IntPtr.Zero,
709 | show = IntPtr.Zero,
710 | text_received = IntPtr.Zero,
711 | file_transfer_recv = IntPtr.Zero,
712 | file_transfer_send = IntPtr.Zero,
713 | file_transfer_progress_indication = IntPtr.Zero,
714 | network_reachable = IntPtr.Zero,
715 | log_collection_upload_state_changed = IntPtr.Zero,
716 | log_collection_upload_progress_indication = IntPtr.Zero,
717 | friend_list_created = IntPtr.Zero,
718 | friend_list_removed = IntPtr.Zero,
719 | user_data = IntPtr.Zero
720 | };
721 | #pragma warning restore 0612
722 |
723 | vtablePtr = Marshal.AllocHGlobal(Marshal.SizeOf(vtable));
724 | Marshal.StructureToPtr(vtable, vtablePtr, false);
725 |
726 | linphoneCore = linphone_core_new(vtablePtr, null, null, IntPtr.Zero);
727 |
728 | coreLoop = new Thread(LinphoneMainLoop);
729 | coreLoop.IsBackground = false;
730 | coreLoop.Start();
731 |
732 | t_config = new LCSipTransports()
733 | {
734 | udp_port = LC_SIP_TRANSPORT_RANDOM,
735 | tcp_port = LC_SIP_TRANSPORT_RANDOM,
736 | dtls_port = LC_SIP_TRANSPORT_RANDOM,
737 | tls_port = LC_SIP_TRANSPORT_RANDOM
738 | };
739 | t_configPtr = Marshal.AllocHGlobal(Marshal.SizeOf(t_config));
740 | Marshal.StructureToPtr (t_config, t_configPtr, false);
741 | linphone_core_set_sip_transports (linphoneCore, t_configPtr);
742 |
743 | linphone_core_set_user_agent (linphoneCore, agent, version);
744 |
745 | callsDefaultParams = linphone_core_create_call_params (linphoneCore, IntPtr.Zero);
746 | linphone_call_params_enable_video (callsDefaultParams, false);
747 | linphone_call_params_enable_audio (callsDefaultParams, true);
748 | linphone_call_params_enable_early_media_sending (callsDefaultParams, true);
749 |
750 | identity = "sip:" + username + "@" + server;
751 | server_addr = "sip:" + server + ":" + port.ToString();
752 |
753 | auth_info = linphone_auth_info_new (username, null, password, null, null, null);
754 | linphone_core_add_auth_info (linphoneCore, auth_info);
755 |
756 | proxy_cfg = linphone_core_create_proxy_config(linphoneCore);
757 | linphone_proxy_config_set_identity (proxy_cfg, identity);
758 | linphone_proxy_config_set_server_addr (proxy_cfg, server_addr);
759 | linphone_proxy_config_enable_register (proxy_cfg, true);
760 | linphone_core_add_proxy_config (linphoneCore, proxy_cfg);
761 | linphone_core_set_default_proxy_config (linphoneCore, proxy_cfg);
762 | }
763 |
764 | public void DestroyPhone ()
765 | {
766 | if (RegistrationStateChangedEvent != null)
767 | RegistrationStateChangedEvent(LinphoneRegistrationState.LinphoneRegistrationProgress); // disconnecting
768 |
769 | linphone_core_terminate_all_calls (linphoneCore);
770 |
771 | SetTimeout (delegate {
772 | linphone_call_params_unref (callsDefaultParams);
773 |
774 | if (linphone_proxy_config_is_registered (proxy_cfg)) {
775 | linphone_proxy_config_edit (proxy_cfg);
776 | linphone_proxy_config_enable_register (proxy_cfg, false);
777 | linphone_proxy_config_done (proxy_cfg);
778 | }
779 |
780 | SetTimeout (delegate {
781 | running = false;
782 | }, 10000);
783 |
784 | }, 5000);
785 | }
786 |
787 | void LinphoneMainLoop()
788 | {
789 | while (running)
790 | {
791 | linphone_core_iterate(linphoneCore); // roll
792 | System.Threading.Thread.Sleep(100);
793 | }
794 |
795 | linphone_core_unref(linphoneCore);
796 |
797 | if (vtablePtr != IntPtr.Zero)
798 | Marshal.FreeHGlobal(vtablePtr);
799 | if (t_configPtr != IntPtr.Zero)
800 | Marshal.FreeHGlobal(t_configPtr);
801 | registration_state_changed = null;
802 | call_state_changed = null;
803 | linphoneCore = callsDefaultParams = proxy_cfg = auth_info = t_configPtr = IntPtr.Zero;
804 | coreLoop = null;
805 | identity = null;
806 | server_addr = null;
807 |
808 | if (RegistrationStateChangedEvent != null)
809 | RegistrationStateChangedEvent(LinphoneRegistrationState.LinphoneRegistrationCleared);
810 |
811 | }
812 |
813 | public void SendDTMFs (Call call, string dtmfs)
814 | {
815 | if (call == null)
816 | throw new ArgumentNullException("call");
817 |
818 | if (linphoneCore == IntPtr.Zero || !running)
819 | {
820 | if (ErrorEvent != null)
821 | ErrorEvent(call, "Cannot make or receive calls when Linphone Core is not working.");
822 | return;
823 | }
824 |
825 | LinphoneCall linphonecall = (LinphoneCall)call;
826 | linphone_call_send_dtmfs (linphonecall.LinphoneCallPtr, dtmfs);
827 | }
828 |
829 | public void SetRingbackSound (string file)
830 | {
831 | if (linphoneCore == IntPtr.Zero || !running)
832 | {
833 | if (ErrorEvent != null)
834 | ErrorEvent(null, "Cannot modify configuration when Linphone Core is not working.");
835 | return;
836 | }
837 |
838 | linphone_core_set_ringback (linphoneCore, file);
839 | }
840 |
841 | public void SetIncomingRingSound (string file)
842 | {
843 | if (linphoneCore == IntPtr.Zero || !running)
844 | {
845 | if (ErrorEvent != null)
846 | ErrorEvent(null, "Cannot modify configuration when Linphone Core is not working.");
847 | return;
848 | }
849 |
850 | linphone_core_set_ring (linphoneCore, file);
851 | }
852 |
853 | public void TerminateCall (Call call)
854 | {
855 | if (call == null)
856 | throw new ArgumentNullException ("call");
857 |
858 | if (linphoneCore == IntPtr.Zero || !running) {
859 | if (ErrorEvent != null)
860 | ErrorEvent (call, "Cannot make or receive calls when Linphone Core is not working.");
861 | return;
862 | }
863 |
864 | LinphoneCall linphonecall = (LinphoneCall) call;
865 | linphone_core_terminate_call (linphoneCore, linphonecall.LinphoneCallPtr);
866 | }
867 |
868 | public void TerminateAllCalls()
869 | {
870 | if (linphoneCore == IntPtr.Zero || !running) {
871 | if (ErrorEvent != null)
872 | ErrorEvent (null, "Cannot terminate calls when Linphone Core is not working.");
873 | return;
874 | }
875 | linphone_core_terminate_all_calls(linphoneCore);
876 | }
877 |
878 | public void MakeCall (string uri)
879 | {
880 | if (linphoneCore == IntPtr.Zero || !running) {
881 | if (ErrorEvent != null)
882 | ErrorEvent (null, "Cannot make or receive calls when Linphone Core is not working.");
883 | return;
884 | }
885 |
886 | IntPtr call = linphone_core_invite_with_params (linphoneCore, uri, callsDefaultParams);
887 |
888 | if (call == IntPtr.Zero) {
889 | if (ErrorEvent != null)
890 | ErrorEvent (null, "Cannot call.");
891 | return;
892 | }
893 |
894 | linphone_call_ref(call);
895 | }
896 |
897 | public void MakeCallAndRecord (string uri, string filename)
898 | {
899 | if (linphoneCore == IntPtr.Zero || !running) {
900 | if (ErrorEvent != null)
901 | ErrorEvent (null, "Cannot make or receive calls when Linphone Core is not working.");
902 | return;
903 | }
904 |
905 | linphone_call_params_set_record_file (callsDefaultParams, filename);
906 |
907 | IntPtr call = linphone_core_invite_with_params (linphoneCore, uri, callsDefaultParams);
908 | if (call == IntPtr.Zero) {
909 | if (ErrorEvent != null)
910 | ErrorEvent (null, "Cannot call.");
911 | return;
912 | }
913 |
914 | linphone_call_ref(call);
915 | linphone_call_start_recording (call);
916 | }
917 |
918 | public void ReceiveCallAndRecord (Call call, string filename)
919 | {
920 | if (call == null)
921 | throw new ArgumentNullException ("call");
922 |
923 | if (linphoneCore == IntPtr.Zero || !running) {
924 | if (ErrorEvent != null)
925 | ErrorEvent (call, "Cannot make or receive calls when Linphone Core is not working.");
926 | return;
927 | }
928 |
929 | LinphoneCall linphonecall = (LinphoneCall) call;
930 | linphone_call_ref(linphonecall.LinphoneCallPtr);
931 | linphone_call_params_set_record_file (callsDefaultParams, filename);
932 | linphone_core_accept_call_with_params (linphoneCore, linphonecall.LinphoneCallPtr, callsDefaultParams);
933 | linphone_call_start_recording (linphonecall.LinphoneCallPtr);
934 | }
935 |
936 | public void ReceiveCall (Call call)
937 | {
938 | if (call == null)
939 | throw new ArgumentNullException ("call");
940 |
941 | if (linphoneCore == IntPtr.Zero || !running) {
942 | if (ErrorEvent != null)
943 | ErrorEvent (call, "Cannot receive call when Linphone Core is not working.");
944 | return;
945 | }
946 |
947 | LinphoneCall linphonecall = (LinphoneCall) call;
948 | linphone_call_ref(linphonecall.LinphoneCallPtr);
949 | linphone_call_params_set_record_file (callsDefaultParams, null);
950 | linphone_core_accept_call_with_params (linphoneCore, linphonecall.LinphoneCallPtr, callsDefaultParams);
951 | }
952 |
953 | public delegate void RegistrationStateChangedDelegate (LinphoneRegistrationState state);
954 | public event RegistrationStateChangedDelegate RegistrationStateChangedEvent;
955 |
956 | public delegate void CallStateChangedDelegate (Call call);
957 | public event CallStateChangedDelegate CallStateChangedEvent;
958 |
959 | public delegate void ErrorDelegate (Call call, string message);
960 | public event ErrorDelegate ErrorEvent;
961 |
962 | void OnRegistrationChanged (IntPtr lc, IntPtr cfg, LinphoneRegistrationState cstate, string message)
963 | {
964 | if (linphoneCore == IntPtr.Zero || !running) return;
965 | #if (TRACE)
966 | Console.WriteLine("OnRegistrationChanged: {0}", cstate);
967 | #endif
968 | if (RegistrationStateChangedEvent != null)
969 | RegistrationStateChangedEvent (cstate);
970 | }
971 |
972 | void OnCallStateChanged (IntPtr lc, IntPtr call, LinphoneCallState cstate, string message)
973 | {
974 | if (linphoneCore == IntPtr.Zero || !running) return;
975 | #if (TRACE)
976 | Console.WriteLine("OnCallStateChanged: {0}", cstate);
977 | #endif
978 |
979 | Call.CallState newstate = Call.CallState.None;
980 | Call.CallType newtype = Call.CallType.None;
981 | string from = "";
982 | string to = "";
983 | IntPtr addressStringPtr;
984 |
985 | // detecting direction, state and source-destination data by state
986 | switch (cstate) {
987 | case LinphoneCallState.LinphoneCallIncomingReceived:
988 | case LinphoneCallState.LinphoneCallIncomingEarlyMedia:
989 | newstate = Call.CallState.Loading;
990 | newtype = Call.CallType.Incoming;
991 | addressStringPtr = linphone_call_get_remote_address_as_string(call);
992 | if (addressStringPtr != IntPtr.Zero) from = Marshal.PtrToStringAnsi(addressStringPtr);
993 | to = identity;
994 | break;
995 |
996 | case LinphoneCallState.LinphoneCallConnected:
997 | case LinphoneCallState.LinphoneCallStreamsRunning:
998 | case LinphoneCallState.LinphoneCallPausedByRemote:
999 | case LinphoneCallState.LinphoneCallUpdatedByRemote:
1000 | newstate = Call.CallState.Active;
1001 | break;
1002 |
1003 | case LinphoneCallState.LinphoneCallOutgoingInit:
1004 | case LinphoneCallState.LinphoneCallOutgoingProgress:
1005 | case LinphoneCallState.LinphoneCallOutgoingRinging:
1006 | case LinphoneCallState.LinphoneCallOutgoingEarlyMedia:
1007 | newstate = Call.CallState.Loading;
1008 | newtype = Call.CallType.Outcoming;
1009 | addressStringPtr = linphone_call_get_remote_address_as_string(call);
1010 | if (addressStringPtr != IntPtr.Zero) to = Marshal.PtrToStringAnsi(addressStringPtr);
1011 | from = this.identity;
1012 | break;
1013 |
1014 | case LinphoneCallState.LinphoneCallError:
1015 | newstate = Call.CallState.Error;
1016 | break;
1017 |
1018 | case LinphoneCallState.LinphoneCallReleased:
1019 | case LinphoneCallState.LinphoneCallEnd:
1020 | newstate = Call.CallState.Completed;
1021 | if (linphone_call_params_get_record_file (callsDefaultParams) != IntPtr.Zero)
1022 | linphone_call_stop_recording (call);
1023 | break;
1024 |
1025 | default:
1026 | break;
1027 | }
1028 |
1029 | IntPtr callref = linphone_call_ref(call);
1030 |
1031 | LinphoneCall existCall = FindCall (callref);
1032 |
1033 | if (existCall == null) {
1034 | existCall = new LinphoneCall ();
1035 | existCall.SetCallState (newstate);
1036 | existCall.SetCallType (newtype);
1037 | existCall.SetFrom (from);
1038 | existCall.SetTo (to);
1039 | existCall.LinphoneCallPtr = callref;
1040 |
1041 | calls.Add (existCall);
1042 |
1043 | if (CallStateChangedEvent != null)
1044 | CallStateChangedEvent (existCall);
1045 | } else {
1046 | if (existCall.GetState () != newstate) {
1047 | existCall.SetCallState (newstate);
1048 | if (CallStateChangedEvent != null)
1049 | CallStateChangedEvent(existCall);
1050 | }
1051 | }
1052 |
1053 | if (cstate == LinphoneCallState.LinphoneCallReleased)
1054 | {
1055 | linphone_call_unref(existCall.LinphoneCallPtr);
1056 | calls.Remove(existCall);
1057 | }
1058 | }
1059 |
1060 | }
1061 | }
1062 |
1063 |
--------------------------------------------------------------------------------
/Phone.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Diagnostics;
3 |
4 | namespace sipdotnet
5 | {
6 | public class Phone
7 | {
8 | public enum ConnectState
9 | {
10 | Disconnected, // idle
11 | Progress, // registering on server
12 | Connected // successfull registered
13 | };
14 | ConnectState connectState;
15 |
16 | public ConnectState CurrentConnectState {
17 | get {
18 | return connectState;
19 | }
20 | }
21 |
22 | public enum LineState
23 | {
24 | Free, // no active calls
25 | Busy // any call
26 | };
27 | LineState lineState;
28 |
29 | public LineState CurrentLineState {
30 | get {
31 | return lineState;
32 | }
33 | }
34 |
35 | public enum Error
36 | {
37 | ///
38 | /// Registration error
39 | ///
40 | RegisterFailed,
41 |
42 | ///
43 | /// Trying to make/receive call while another call is active
44 | ///
45 | LineIsBusyError,
46 |
47 | ///
48 | /// Trying to connect while connected / connecting or disconnect when not connected
49 | ///
50 | OrderError,
51 |
52 | ///
53 | /// Call failed
54 | ///
55 | CallError,
56 | UnknownError
57 | };
58 |
59 | ///
60 | /// Successful registered
61 | ///
62 | public delegate void OnPhoneConnected ();
63 |
64 | ///
65 | /// Successful unregistered
66 | ///
67 | public delegate void OnPhoneDisconnected ();
68 |
69 | ///
70 | /// Phone is ringing
71 | ///
72 | ///
73 | public delegate void OnIncomingCall (Call call);
74 |
75 | ///
76 | /// Link is established
77 | ///
78 | ///
79 | public delegate void OnCallActive (Call call);
80 |
81 | ///
82 | /// Call completed
83 | ///
84 | ///
85 | public delegate void OnCallCompleted (Call call);
86 |
87 | ///
88 | /// Error notification
89 | ///
90 | ///
91 | ///
92 | public delegate void OnError (Call call, Error error);
93 |
94 | public event OnPhoneConnected PhoneConnectedEvent;
95 | public event OnPhoneDisconnected PhoneDisconnectedEvent;
96 | public event OnIncomingCall IncomingCallEvent;
97 | public event OnCallActive CallActiveEvent;
98 | public event OnCallCompleted CallCompletedEvent;
99 | public event OnError ErrorEvent;
100 |
101 | Account account;
102 |
103 | public Account Account {
104 | get {
105 | return account;
106 | }
107 | }
108 |
109 | string useragent = "liblinphone";
110 |
111 | public string Useragent {
112 | get {
113 | return useragent;
114 | }
115 | set {
116 | useragent = value;
117 | }
118 | }
119 |
120 | string version = "6.0.0";
121 |
122 | public string Version {
123 | get {
124 | return version;
125 | }
126 | set {
127 | version = value;
128 | }
129 | }
130 |
131 | Linphone linphone;
132 |
133 | public Phone (Account account)
134 | {
135 | Debug.Assert (null != account, "Phone requires an Account to make calls.");
136 | this.account = account;
137 | linphone = new Linphone ();
138 | linphone.RegistrationStateChangedEvent += (Linphone.LinphoneRegistrationState state) => {
139 | switch (state) {
140 | case Linphone.LinphoneRegistrationState.LinphoneRegistrationProgress:
141 | connectState = ConnectState.Progress;
142 | break;
143 |
144 | case Linphone.LinphoneRegistrationState.LinphoneRegistrationFailed:
145 | linphone.DestroyPhone();
146 | if (ErrorEvent != null) ErrorEvent (null, Error.RegisterFailed);
147 | break;
148 |
149 | case Linphone.LinphoneRegistrationState.LinphoneRegistrationCleared:
150 | connectState = ConnectState.Disconnected;
151 | if (PhoneDisconnectedEvent != null) PhoneDisconnectedEvent();
152 | break;
153 |
154 | case Linphone.LinphoneRegistrationState.LinphoneRegistrationOk:
155 | connectState = ConnectState.Connected;
156 | if (PhoneConnectedEvent != null) PhoneConnectedEvent();
157 | break;
158 |
159 | case Linphone.LinphoneRegistrationState.LinphoneRegistrationNone:
160 | default:
161 | break;
162 | }
163 | };
164 |
165 | linphone.ErrorEvent += (call, message) => {
166 | Console.WriteLine ("Error: {0}", message);
167 | if (ErrorEvent != null) ErrorEvent (call, Error.UnknownError);
168 | };
169 |
170 | linphone.CallStateChangedEvent += (Call call) => {
171 | Call.CallState state = call.GetState();
172 |
173 | switch (state) {
174 | case Call.CallState.Active:
175 | lineState = LineState.Busy;
176 | if (CallActiveEvent != null)
177 | CallActiveEvent (call);
178 | break;
179 |
180 | case Call.CallState.Loading:
181 | lineState = LineState.Busy;
182 | if (call.GetCallType () == Call.CallType.Incoming)
183 | if (IncomingCallEvent != null)
184 | IncomingCallEvent (call);
185 | break;
186 |
187 | case Call.CallState.Error:
188 | this.lineState = LineState.Free;
189 | if (ErrorEvent != null)
190 | ErrorEvent(null, Error.CallError);
191 | break;
192 |
193 | case Call.CallState.Completed:
194 | default:
195 | this.lineState = LineState.Free;
196 | if (CallCompletedEvent != null)
197 | CallCompletedEvent (call);
198 | break;
199 | }
200 |
201 | };
202 | }
203 |
204 | public void Connect ()
205 | {
206 | if (connectState == ConnectState.Disconnected)
207 | {
208 | connectState = ConnectState.Progress;
209 | linphone.CreatePhone(account.Username, Account.Password, Account.Server, Account.Port, Useragent, Version);
210 | }
211 | else
212 | if (ErrorEvent != null) ErrorEvent(null, Error.OrderError);
213 | }
214 |
215 | public void Disconnect ()
216 | {
217 | if (connectState == ConnectState.Connected)
218 | linphone.DestroyPhone ();
219 | else
220 | if (ErrorEvent != null) ErrorEvent (null, Error.OrderError);
221 | }
222 |
223 | public void MakeCall (string sipUriOrPhone)
224 | {
225 | if (string.IsNullOrEmpty(sipUriOrPhone))
226 | throw new ArgumentNullException ("sipUriOrPhone");
227 |
228 | if (connectState != ConnectState.Connected)
229 | throw new InvalidOperationException("not connected");
230 |
231 | if (lineState == LineState.Free)
232 | linphone.MakeCall (sipUriOrPhone);
233 | else {
234 | if (ErrorEvent != null)
235 | ErrorEvent (null, Error.LineIsBusyError);
236 | }
237 | }
238 |
239 | public void MakeCallAndRecord (string sipUriOrPhone, string filename)
240 | {
241 | if (string.IsNullOrEmpty(sipUriOrPhone))
242 | throw new ArgumentNullException ("sipUriOrPhone");
243 |
244 | if (string.IsNullOrEmpty(filename))
245 | throw new ArgumentNullException ("filename");
246 |
247 | if (connectState != ConnectState.Connected)
248 | throw new InvalidOperationException("not connected");
249 |
250 | if (lineState == LineState.Free)
251 | linphone.MakeCallAndRecord (sipUriOrPhone, filename);
252 | else {
253 | if (ErrorEvent != null)
254 | ErrorEvent (null, Error.LineIsBusyError);
255 | }
256 | }
257 |
258 | public void ReceiveCallAndRecord (Call call, string filename)
259 | {
260 | if (connectState != ConnectState.Connected)
261 | throw new InvalidOperationException("not connected");
262 | if (call == null)
263 | throw new ArgumentNullException ("call");
264 | if (string.IsNullOrEmpty(filename))
265 | throw new ArgumentNullException ("filename");
266 |
267 | linphone.ReceiveCallAndRecord (call, filename);
268 | }
269 |
270 | public void ReceiveCall (Call call)
271 | {
272 | if (connectState != ConnectState.Connected)
273 | throw new InvalidOperationException("not connected");
274 | if (call == null)
275 | throw new ArgumentNullException ("call");
276 |
277 | linphone.ReceiveCall (call);
278 | }
279 |
280 | public void TerminateCall (Call call)
281 | {
282 | if (connectState != ConnectState.Connected)
283 | throw new InvalidOperationException("not connected");
284 | if (call == null)
285 | throw new ArgumentNullException ("call");
286 |
287 | linphone.TerminateCall (call);
288 | }
289 |
290 | public void SendDTMFs (Call call, string dtmfs)
291 | {
292 | if (connectState != ConnectState.Connected)
293 | throw new InvalidOperationException("not connected");
294 | if (call == null)
295 | throw new ArgumentNullException("call");
296 | if (string.IsNullOrEmpty(dtmfs))
297 | throw new ArgumentNullException("dtmfs");
298 |
299 | linphone.SendDTMFs (call, dtmfs);
300 | }
301 |
302 | public void SetIncomingRingSound (string filename)
303 | {
304 | if (linphone == null)
305 | throw new InvalidOperationException("not connected");
306 |
307 | linphone.SetIncomingRingSound (filename);
308 | }
309 |
310 | public void SetRingbackSound (string filename)
311 | {
312 | if (linphone == null)
313 | throw new InvalidOperationException("not connected");
314 |
315 | linphone.SetRingbackSound (filename);
316 | }
317 |
318 | }
319 | }
320 |
321 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | sipdotnet
2 | =========
3 |
4 | Small .NET wrapper for [liblinphone](http://www.linphone.org/eng/documentation/dev/liblinphone-free-sip-voip-sdk.html) library. You can use it for simple interface for SIP telephony.
5 |
6 | Using
7 | -----
8 |
9 | You can access to wrapped liblinphone actions and events by `Phone` class. Only that you need is the SIP account (`Account` class).
10 |
11 | For using library from .NET code you need to copy linphone dlls ([liblinphone and dependencies](https://github.com/bedefaced/sipdotnet#requirements)) to directory with your EXE file (or use, for example, [Add item](https://msdn.microsoft.com/en-us/library/9f4t9t92(v=vs.100).aspx) dialog in Visual Studio (and select *Copy to Output*: `Copy if newer` or `Copy always` in `Properties` window of added dlls)) and then add `sipdotnet.dll` (or the whole project) as dependency to your solution.
12 |
13 | Current available functionality:
14 |
15 | - SIP-proxy connection control:
16 | - `Connect`
17 | - `Disconnect`
18 | - `Useragent` and `Version` definition
19 | - Call / Register / Error events:
20 | - `PhoneConnectedEvent`
21 | - `PhoneDisconnectedEvent`
22 | - `IncomingCallEvent`
23 | - `CallActiveEvent`
24 | - `CallCompletedEvent`
25 | - `ErrorEvent`
26 | - Make / receive / terminate / record calls:
27 | - `MakeCall`
28 | - `MakeCallAndRecord`
29 | - `ReceiveCall`
30 | - `ReceiveCallAndRecord`
31 | - `TerminateCall`
32 | - `TerminateAllCalls`
33 |
34 | - Utilities:
35 | - `SendDTMFs` (sending DTMF-tones)
36 | - `SetIncomingRingSound` and `SetRingbackSound` (sound that is heard when it's ringing to remote party)
37 |
38 | Example
39 | -------
40 | ```cs
41 | Account account = new Account ("username", "password", "server");
42 | Phone phone = new Phone (account);
43 | phone.PhoneConnectedEvent += delegate() {
44 | Console.WriteLine("Phone connected. Calling...");
45 | phone.MakeCallAndRecord("phonenumber", "/tmp/filename.wav");
46 | };
47 | phone.CallActiveEvent += delegate(Call call) {
48 | Console.WriteLine("Answered. Call is active!");
49 | };
50 | phone.CallCompletedEvent += delegate(Call call) {
51 | Console.WriteLine("Completed.");
52 | };
53 | phone.Connect (); // connecting
54 | Console.ReadLine ();
55 | Console.WriteLine("Disconnecting...");
56 | phone.Disconnect (); // terminate all calls and disconnect
57 | ```
58 |
59 | Requirements
60 | ------------
61 |
62 | * .NET 4.0 framework on Windows or Linux (>= Mono 3.2.8)
63 | * last available (4.1.1) liblinphone library binaries installed
64 |
65 | Liblinphone on Windows
66 | ----------------------
67 |
68 | Due to backwardness of [SDK binaries](http://www.linphone.org/technical-corner/liblinphone/downloads) version it's _recommended_ to use dlls from [Linphone desktop build](http://www.linphone.org/technical-corner/linphone/downloads). You can use my zipped collection in [lib](https://github.com/bedefaced/sipdotnet/blob/master/lib) directory or collect necessary dlls yourself using such tools as [Dependency Walker](http://www.dependencywalker.com/) against 'linphone.dll'.
69 |
70 | Liblinphone on Linux
71 | --------------------
72 | 1) Install [Mono](http://www.mono-project.com/download/#download-lin)
73 | 2) Build manually from [sources](https://github.com/BelledonneCommunications/linphone), or use [my bash script](https://gist.github.com/bedefaced/3dc4e58c700dada43054f49a3053dcad) for Ubuntu 16.04.
74 |
75 | License
76 | -------
77 | [LGPLv3](http://en.wikipedia.org/wiki/GNU_Lesser_General_Public_License) (see `LICENSE` file)
78 |
--------------------------------------------------------------------------------
/lib/linphone.zip:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/bedefaced/sipdotnet/08602970ae053c9875abc6bedd99d285fffa1e0e/lib/linphone.zip
--------------------------------------------------------------------------------
/sipdotnet.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | Debug
5 | AnyCPU
6 | 10.0.0
7 | 2.0
8 | {BCB250F8-F74C-4768-8833-0F13FC8659B8}
9 | Library
10 | sipdotnet
11 | sipdotnet
12 |
13 |
14 | true
15 | full
16 | false
17 | bin\Debug
18 | DEBUG;WINDOWS
19 | prompt
20 | 4
21 | false
22 | true
23 | AnyCPU
24 |
25 |
26 | full
27 | true
28 | bin\Release
29 | prompt
30 | 4
31 | false
32 | true
33 | WINDOWS
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
--------------------------------------------------------------------------------