Skip to content

The SIPSorcery project is an experiment into the depths of the Session Initiation Protocol (SIP). The project is a combination of the source code available here on GitHub and a hosted service.

License

Notifications You must be signed in to change notification settings

bleissem/sipsorcery

 
 

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

Target SIPSorcery Examples Softphone
net452 Build status
netstandard2.0
dotnetcore3.1
Examples build status
Softphone build status

This repository contains the source for a C# .NET library with full support for the Session Initiation Protocol (SIP) and the Real-time Transport Protocol (RTP).

This library does NOT provide any media (audio and video) handling. There are some limited capabilities in the separate SIPSorceryMedia project but they are Windows specific and not suitable for production. This project can be used for SIP signalling and to send and receive RTP packets. To playback audio/video additional libraries such as NAudio are required.

NEW (Feb 2020): Pre-release support for Web Real-Time Communication (WebRTC) for early adopters. See Getting Started WebRTC.

Note unlike a lot of WebRTC libraries this one is not wrapping Google's WebRTC library and it is also currently missing large blocks of functionality compared to Google's. If you require a dotnet library that provides functionality equivalent to Google's take a look at Microsoft's MixedReality-WebRTC project (despite the name it's not just for Hololens).

Installation

The library is compliant with .NET Standard 2.0, .Net Core 3.1 and .NET Framework 4.5.2. It is available via NuGet.

For .NET Core:

dotnet add package SIPSorcery

With Visual Studio Package Manager Console (or search for SIPSorcery on NuGet):

Install-Package SIPSorcery

Documentation

Class reference documentation and articles explaining common usage are available at https://sipsorcery.github.io/sipsorcery/.

Getting Started WebRTC

Install both the SIPSorcery and SIPSorceryMedia nuget packages.

dotnet add package SIPSorcery --version "4.0.13-pre"
dotnet add package SIPSorceryMedia --version "4.0.13-pre"

The SIPSorceryMedia package wraps access to a number of open source libraries to provide the underlying WebRTC infrastructure for DTLS, SRTP, VPX Codecs as well as the Windows Media Foundation for audio/video capture device access.

The SIPSorcery.Net.WebRtcSession class can be used to create and manage connections with a WebRTC peer which will typically be in the form of a Browser such as Chrome or Firefox.

There are 3 example applications which demonstrate different use cases:

  • WebRTCTestPatternServer: The simplest example. This program serves up a test pattern video stream to a WebRTC peer.

Test pattern example screenshot

  • WebRTCServer: This example extends the test pattern example and can act as a media source for a peer. It has two source options:
    • An mp4 file.
    • Capture devices (webcam and microphone). The example includes an html file which runs in a Browser and will connect to a sample program running on the same machine.

MP4 server example screenshot

  • WebRTCReceiver: A receive only example. It attempts to connect to a WebRTC peer and display the video stream that it receives.

Receive example screenshot

The WebRtcSession class and all WebRTC functionality in this library are still under heavy development. There are large blocks of functionality still missing, particularly ICE and codec support. All issues and PR's are very welcome.

Getting Started

The examples folder contains full sample code designed to demonstrate some common use cases. The GetStarted example which places a SIP call to sip:time@sipsorcery.com is the best place to start and the main program is shown below.

using System;
using System.Net.Sockets;
using System.Threading.Tasks;
using NAudio.Wave;
using Serilog;
using SIPSorcery.SIP;
using SIPSorcery.SIP.App;
using SIPSorcery.Net;

namespace demo
{
    class Program
    {
        private static WaveFormat _waveFormat = new WaveFormat(8000, 16, 1);  // PCMU format used by both input and output streams.
        private static int INPUT_SAMPLE_PERIOD_MILLISECONDS = 20;           // This sets the frequency of the RTP packets.
        private static string DESTINATION = "time@sipsorcery.com";

        static async Task Main()
        {
            Console.WriteLine("SIPSorcery Getting Started Demo");

            AddConsoleLogger();

            var sipTransport = new SIPTransport();
            var userAgent = new SIPUserAgent(sipTransport, null);
            var rtpSession = new RTPMediaSession((int)SDPMediaFormatsEnum.PCMU, AddressFamily.InterNetwork);

            // Connect audio devices to RTP session.
            WaveInEvent microphone = GetAudioInputDevice();
            var speaker = GetAudioOutputDevice();
            ConnectAudioDevicesToRtp(rtpSession, microphone, speaker);

            // Place the call and wait for the result.
            bool callResult = await userAgent.Call(DESTINATION, null, null, rtpSession);

            if(callResult)
            {
                Console.WriteLine("Call attempt successful.");
                microphone.StartRecording();
            }
            else
            {
                Console.WriteLine("Call attempt failed.");
            }
          
            Console.WriteLine("press any key to exit...");
            Console.Read();

            if (userAgent.IsCallActive)
            {
                Console.WriteLine("Hanging up.");
                userAgent.Hangup();
            }

            // Clean up.
            microphone.StopRecording();
            sipTransport.Shutdown();
            SIPSorcery.Net.DNSManager.Stop();
        }

        /// <summary>
        /// Connects the RTP packets we receive to the speaker and sends RTP packets for microphone samples.
        /// </summary>
        /// <param name="rtpSession">The RTP session to use for sending and receiving.</param>
        /// <param name="microphone">The default system  audio input device found.</param>
        /// <param name="speaker">The default system audio output device.</param>
        private static void ConnectAudioDevicesToRtp(RTPMediaSession rtpSession, WaveInEvent microphone, BufferedWaveProvider speaker)
        {
            // Wire up the RTP send session to the audio input device.
            uint rtpSendTimestamp = 0;
            microphone.DataAvailable += (object sender, WaveInEventArgs args) =>
            {
                byte[] sample = new byte[args.Buffer.Length / 2];
                int sampleIndex = 0;

                for (int index = 0; index < args.BytesRecorded; index += 2)
                {
                    var ulawByte = NAudio.Codecs.MuLawEncoder.LinearToMuLawSample(BitConverter.ToInt16(args.Buffer, index));
                    sample[sampleIndex++] = ulawByte;
                }

                if (rtpSession.DestinationEndPoint != null)
                {
                    rtpSession.SendAudioFrame(rtpSendTimestamp, sample);
                    rtpSendTimestamp += (uint)(8000 / microphone.BufferMilliseconds);
                }
            };

            // Wire up the RTP receive session to the audio output device.
            rtpSession.OnReceivedSampleReady += (sample) =>
            {
                for (int index = 0; index < sample.Length; index++)
                {
                    short pcm = NAudio.Codecs.MuLawDecoder.MuLawToLinearSample(sample[index]);
                    byte[] pcmSample = new byte[] { (byte)(pcm & 0xFF), (byte)(pcm >> 8) };
                    speaker.AddSamples(pcmSample, 0, 2);
                }
            };
        }

        /// <summary>
        /// Get the audio output device, e.g. speaker.
        /// Note that NAudio.Wave.WaveOut is not available for .Net Standard so no easy way to check if 
        /// there's a speaker.
        /// </summary>
        private static BufferedWaveProvider GetAudioOutputDevice()
        {
            WaveOutEvent waveOutEvent = new WaveOutEvent();
            var waveProvider = new BufferedWaveProvider(_waveFormat);
            waveProvider.DiscardOnBufferOverflow = true;
            waveOutEvent.Init(waveProvider);
            waveOutEvent.Play();

            return waveProvider;
        }

        /// <summary>
        /// Get the audio input device, e.g. microphone. The input device that will provide 
        /// audio samples that can be encoded, packaged into RTP and sent to the remote call party.
        /// </summary>
        private static WaveInEvent GetAudioInputDevice()
        {
            if (WaveInEvent.DeviceCount == 0)
            {
                throw new ApplicationException("No audio input devices available. No audio will be sent.");
            }
            else
            {
                WaveInEvent waveInEvent = new WaveInEvent();
                WaveFormat waveFormat = _waveFormat;
                waveInEvent.BufferMilliseconds = INPUT_SAMPLE_PERIOD_MILLISECONDS;
                waveInEvent.NumberOfBuffers = 1;
                waveInEvent.DeviceNumber = 0;
                waveInEvent.WaveFormat = waveFormat;

                return waveInEvent;
            }
        }

        /// <summary>
        ///  Adds a console logger. Can be omitted if internal SIPSorcery debug and warning messages are not required.
        /// </summary>
        private static void AddConsoleLogger()
        {
            var loggerFactory = new Microsoft.Extensions.Logging.LoggerFactory();
            var loggerConfig = new LoggerConfiguration()
                .Enrich.FromLogContext()
                .MinimumLevel.Is(Serilog.Events.LogEventLevel.Information)
                .WriteTo.Console()
                .CreateLogger();
            loggerFactory.AddSerilog(loggerConfig);
            SIPSorcery.Sys.Log.LoggerFactory = loggerFactory;
        }
    }
}

SIP Transport Layer

To use the SIP functionality the first step is to initialise the SIPTransport class. It takes care of things like retransmitting requests and responses, DNS resolution, selecting the next hop for requests, matching SIP messages to transactions and more.

The SIPTransport class can have multiple SIP channels added to it. A SIP channel is roughly the equivalent to the HTTP connection between a Web Browser and Server. It expects all packets received to be either a SIP request or response. The types of SIP channels supported are UDP, TCP, TLS and Web Sockets.

The code below shows how to create a SIPTransport instance and add a single UDP channel to it. If no channel is added to the transport it will attempt to create them on demand.

var sipTransport = new SIPTransport();
var sipChannel = new SIPUDPChannel(IPAddress.Loopback, 5060);
sipTransport.AddSIPChannel(sipChannel);

To shutdown the SIPTransport use:

sipTransport.Shutdown();

SIP User Agent and RTP Session

The easiest way to make use of the SIP Transport layer is to use it with a SIPUserAgent object. The SIPUserAgent takes care of the SIP signalling for most common SIP functions. An RTPMediaSession is needed to handle the sending and receiving of RTP packets and the media (audio/video) information within them.

var userAgent = new SIPUserAgent(sipTransport, null);
var rtpSession = new RTPMediaSession((int)SDPMediaFormatsEnum.PCMU, AddressFamily.InterNetwork);

A call can then be placed using the SIPUserAgent.Call method and if successfully answered then sampling of the local audio input device can be started.

// Place the call and wait for the result.
bool callResult = await userAgent.Call(DESTINATION, null, null, rtpSession);

if(callResult)
{
	Console.WriteLine("Call attempt successful.");
	microphone.StartRecording();
}
else
{
	Console.WriteLine("Call attempt failed.");
}

Next Steps

Additional example programs are provided to demonstrate how to use the SIPSorcery library in some common scenarios. The example programs are in the examples folder.

  • Get Started: Simplest example. Demonstrates how to place a SIP call.

  • SIP Proxy: Very rudimentary example for a SIP Proxy and SIP Registrar.

  • Registration Client: Demonstrates how to use the SIPRegistrationUserAgent class to register with a SIP Registrar server.

  • SIP Call Client: Demonstrates how to use SIPClientUserAgent class to place a call to a SIP server user agent.

  • SIP Call Server: Demonstrates how to use the SIPServerUserAgent class to receive a call from a SIP client user agent.

  • SoftPhone: A very rudimentary SIP softphone implementation.

  • Get Started Web Socket: An example of how to create a web socket listener to send and receive SIP messages. An explanation of the example is available here.

  • STUN Server: An example of how to create a basic STUN (RFC3849) server. An explanation of the example is available here.

  • Call Hold and Blind Transfer: An example of how to place a call on hold and perform a blind transfer using a REFER request as specified in RFC3515. An explanation of the example is available here.

  • Call Attended Transfer: An example of how to perform an attended transfer. An explanation of the example is available here.

  • Send DTMF (as RTP events): An example of how to send DTMF tones using RTP events as specified in RFC2833. An explanation of the example is available here.

About

The SIPSorcery project is an experiment into the depths of the Session Initiation Protocol (SIP). The project is a combination of the source code available here on GitHub and a hosted service.

Resources

License

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published

Languages

  • C# 100.0%