Created
June 26, 2020 07:32
-
-
Save fredldotme/9f33eaeb327f5d2e17c175ff7095281a to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
//----------------------------------------------------------------------------- | |
// Filename: Program.cs | |
// | |
// Description: An abbreviated example program of how to use the SIPSorcery | |
// core library to place a SIP call. The example program depends on one audio | |
// input and one audio output being available. | |
// | |
// Author(s): | |
// Aaron Clauson ([email protected]) | |
// | |
// History: | |
// 26 Oct 2019 Aaron Clauson Created, Dublin, Ireland. | |
// 26 Feb 2020 Aaron Clauson Switched RTP to use RtpAVSession. | |
// | |
// License: | |
// BSD 3-Clause "New" or "Revised" License, see included LICENSE.md file. | |
//----------------------------------------------------------------------------- | |
using System; | |
using System.Collections.Generic; | |
using System.Threading; | |
using System.Threading.Tasks; | |
using Microsoft.Extensions.Logging; | |
using Serilog; | |
using SIPSorcery.Media; | |
using SIPSorcery.Net; | |
using SIPSorcery.SIP; | |
using SIPSorcery.SIP.App; | |
using SIPSorcery.Sys; | |
namespace SIPSorcery | |
{ | |
class Program | |
{ | |
private static readonly string DEFAULT_DESTINATION_SIP_URI = "sip:[email protected]"; // Talking Clock. | |
private static Microsoft.Extensions.Logging.ILogger Log = SIPSorcery.Sys.Log.Logger; | |
private static void OnRtpPacketReceived(SDPMediaTypesEnum mediaType, RTPPacket rtpPacket) | |
{ | |
Console.WriteLine("OnRtpPacketReceived"); | |
} | |
private const int DTMF_EVENT_PAYLOAD_ID = 101; | |
static void Main(string[] args) | |
{ | |
Console.WriteLine("SIPSorcery client user agent example."); | |
Console.WriteLine("Press ctrl-c to exit."); | |
// Plumbing code to facilitate a graceful exit. | |
ManualResetEvent exitMre = new ManualResetEvent(false); | |
bool isCallHungup = false; | |
bool hasCallFailed = false; | |
AddConsoleLogger(); | |
SIPURI callUri = SIPURI.ParseSIPURI(DEFAULT_DESTINATION_SIP_URI); | |
if (args != null && args.Length > 0) | |
{ | |
if (!SIPURI.TryParse(args[0], out callUri)) | |
{ | |
Log.LogWarning($"Command line argument could not be parsed as a SIP URI {args[0]}"); | |
} | |
} | |
Log.LogInformation($"Call destination {callUri}."); | |
// Set up a default SIP transport. | |
var sipTransport = new SIPTransport(); | |
EnableTraceLogs(sipTransport); | |
// Get the IP address the RTP will be sent from. While we can listen on IPAddress.Any | IPv6Any | |
// we can't put 0.0.0.0 or [::0] in the SDP or the callee will treat our RTP stream as inactive. | |
var lookupResult = SIPDNSManager.ResolveSIPService(callUri, false); | |
Log.LogDebug($"DNS lookup result for {callUri}: {lookupResult?.GetSIPEndPoint()}."); | |
var dstAddress = lookupResult.GetSIPEndPoint().Address; | |
var localOfferAddress = NetServices.GetLocalAddressForRemote(dstAddress); | |
// Initialise an RTP session to receive the RTP packets from the remote SIP server. | |
/*var audioOptions = new AudioOptions | |
{ | |
AudioSource = AudioSourcesEnum.CaptureDevice, | |
AudioCodecs = new List<SDPMediaFormatsEnum> { SDPMediaFormatsEnum.PCMA, SDPMediaFormatsEnum.PCMU }, | |
OutputDeviceIndex = AudioOptions.DEFAULT_OUTPUTDEVICE_INDEX | |
};*/ | |
var pcmu = new SDPMediaFormat(SDPMediaFormatsEnum.PCMU); | |
var pcma = new SDPMediaFormat(SDPMediaFormatsEnum.PCMA); | |
int clockRate = pcmu.GetClockRate(); | |
SDPMediaFormat rtpEventFormat = new SDPMediaFormat(DTMF_EVENT_PAYLOAD_ID); | |
rtpEventFormat.SetFormatAttribute($"{SDP.TELEPHONE_EVENT_ATTRIBUTE}/{clockRate}"); | |
rtpEventFormat.SetFormatParameterAttribute("0-16"); | |
var rtpSession = new RTPSession(/*isMediaMultiplexed*/ true, /*isRtcpMultiplexed*/ true, /*isSecure*/ false, /*bindAddress*/ null); | |
var audioCapabilities = new List<SDPMediaFormat> { pcmu, pcma, rtpEventFormat }; | |
MediaStreamTrack audioTrack = new MediaStreamTrack(SDPMediaTypesEnum.audio, false, audioCapabilities); | |
rtpSession.addTrack(audioTrack); | |
Console.WriteLine("Adding this mofucker"); | |
rtpSession.OnRtpPacketReceived += OnRtpPacketReceived; | |
var offerSDP = rtpSession.CreateOffer(localOfferAddress); | |
// Create a client user agent to place a call to a remote SIP server along with event handlers for the different stages of the call. | |
var uac = new SIPClientUserAgent(sipTransport); | |
uac.CallTrying += (uac, resp) => Log.LogInformation($"{uac.CallDescriptor.To} Trying: {resp.StatusCode} {resp.ReasonPhrase}."); | |
uac.CallRinging += (uac, resp) => | |
{ | |
Log.LogInformation($"{uac.CallDescriptor.To} Ringing: {resp.StatusCode} {resp.ReasonPhrase}."); | |
if (resp.Status == SIPResponseStatusCodesEnum.SessionProgress) | |
{ | |
Console.WriteLine("rtpSession.Start();"); | |
rtpSession.Start(); | |
} | |
}; | |
uac.CallFailed += (uac, err, resp) => | |
{ | |
Log.LogWarning($"{uac.CallDescriptor.To} Failed: {err}"); | |
hasCallFailed = true; | |
}; | |
uac.CallAnswered += (iuac, resp) => | |
{ | |
if (resp.Status == SIPResponseStatusCodesEnum.Ok) | |
{ | |
Log.LogInformation($"{uac.CallDescriptor.To} Answered: {resp.StatusCode} {resp.ReasonPhrase}."); | |
var result = rtpSession.SetRemoteDescription(SDP.ParseSDPDescription(resp.Body)); | |
if(result == SetDescriptionResultEnum.OK) | |
{ | |
rtpSession.Start(); | |
} | |
else | |
{ | |
Log.LogWarning($"Failed to set remote description {result}."); | |
uac.Hangup(); | |
} | |
} | |
else | |
{ | |
Log.LogWarning($"{uac.CallDescriptor.To} Answered: {resp.StatusCode} {resp.ReasonPhrase}."); | |
} | |
}; | |
// The only incoming request that needs to be explicitly handled for this example is if the remote end hangs up the call. | |
sipTransport.SIPTransportRequestReceived += async (SIPEndPoint localSIPEndPoint, SIPEndPoint remoteEndPoint, SIPRequest sipRequest) => | |
{ | |
if (sipRequest.Method == SIPMethodsEnum.BYE) | |
{ | |
SIPResponse okResponse = SIPResponse.GetResponse(sipRequest, SIPResponseStatusCodesEnum.Ok, null); | |
await sipTransport.SendResponseAsync(okResponse); | |
if (uac.IsUACAnswered) | |
{ | |
Log.LogInformation("Call was hungup by remote server."); | |
isCallHungup = true; | |
exitMre.Set(); | |
} | |
} | |
}; | |
// Start the thread that places the call. | |
SIPCallDescriptor callDescriptor = new SIPCallDescriptor( | |
SIPConstants.SIP_DEFAULT_USERNAME, | |
null, | |
callUri.ToString(), | |
SIPConstants.SIP_DEFAULT_FROMURI, | |
callUri.CanonicalAddress, | |
null, null, null, | |
SIPCallDirection.Out, | |
SDP.SDP_MIME_CONTENTTYPE, | |
offerSDP != null ? offerSDP.ToString() : "", | |
null); | |
uac.Call(callDescriptor); | |
uac.ServerTransaction.TransactionTraceMessage += (tx, msg) => Log.LogInformation($"UAC tx trace message. {msg}"); | |
// Ctrl-c will gracefully exit the call at any point. | |
Console.CancelKeyPress += delegate (object sender, ConsoleCancelEventArgs e) | |
{ | |
e.Cancel = true; | |
exitMre.Set(); | |
}; | |
// Wait for a signal saying the call failed, was cancelled with ctrl-c or completed. | |
exitMre.WaitOne(); | |
Log.LogInformation("Exiting..."); | |
Console.WriteLine("rtpSession.Close();"); | |
rtpSession.Close(null); | |
if (!isCallHungup && uac != null) | |
{ | |
if (uac.IsUACAnswered) | |
{ | |
Log.LogInformation($"Hanging up call to {uac.CallDescriptor.To}."); | |
uac.Hangup(); | |
} | |
else if (!hasCallFailed) | |
{ | |
Log.LogInformation($"Cancelling call to {uac.CallDescriptor.To}."); | |
uac.Cancel(); | |
} | |
// Give the BYE or CANCEL request time to be transmitted. | |
Log.LogInformation("Waiting 1s for call to clean up..."); | |
Task.Delay(1000).Wait(); | |
} | |
SIPSorcery.Net.DNSManager.Stop(); | |
if (sipTransport != null) | |
{ | |
Log.LogInformation("Shutting down SIP transport..."); | |
sipTransport.Shutdown(); | |
} | |
} | |
/// <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.Debug) | |
.WriteTo.Console() | |
.CreateLogger(); | |
loggerFactory.AddSerilog(loggerConfig); | |
SIPSorcery.Sys.Log.LoggerFactory = loggerFactory; | |
} | |
/// <summary> | |
/// Enable detailed SIP log messages. | |
/// </summary> | |
private static void EnableTraceLogs(SIPTransport sipTransport) | |
{ | |
sipTransport.SIPRequestInTraceEvent += (localEP, remoteEP, req) => | |
{ | |
Log.LogDebug($"Request received: {localEP}<-{remoteEP}"); | |
Log.LogDebug(req.ToString()); | |
}; | |
sipTransport.SIPRequestOutTraceEvent += (localEP, remoteEP, req) => | |
{ | |
Log.LogDebug($"Request sent: {localEP}->{remoteEP}"); | |
Log.LogDebug(req.ToString()); | |
}; | |
sipTransport.SIPResponseInTraceEvent += (localEP, remoteEP, resp) => | |
{ | |
Log.LogDebug($"Response received: {localEP}<-{remoteEP}"); | |
Log.LogDebug(resp.ToString()); | |
}; | |
sipTransport.SIPResponseOutTraceEvent += (localEP, remoteEP, resp) => | |
{ | |
Log.LogDebug($"Response sent: {localEP}->{remoteEP}"); | |
Log.LogDebug(resp.ToString()); | |
}; | |
sipTransport.SIPRequestRetransmitTraceEvent += (tx, req, count) => | |
{ | |
Log.LogDebug($"Request retransmit {count} for request {req.StatusLine}, initial transmit {DateTime.Now.Subtract(tx.InitialTransmit).TotalSeconds.ToString("0.###")}s ago."); | |
}; | |
sipTransport.SIPResponseRetransmitTraceEvent += (tx, resp, count) => | |
{ | |
Log.LogDebug($"Response retransmit {count} for response {resp.ShortDescription}, initial transmit {DateTime.Now.Subtract(tx.InitialTransmit).TotalSeconds.ToString("0.###")}s ago."); | |
}; | |
} | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment