Skip to content

Commit cb13c40

Browse files
committed
Added RTP session to user agent. Messed around with the softphone UI.
1 parent 9da2d88 commit cb13c40

File tree

12 files changed

+421
-552
lines changed

12 files changed

+421
-552
lines changed

.editorconfig

+1-1
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ dotnet_separate_import_directive_groups = false
2020
csharp_space_after_keywords_in_control_flow_statements = true
2121

2222
# Wrapping preferences
23-
csharp_preserve_single_line_statements = false
23+
csharp_preserve_single_line_statements = true
2424
csharp_preserve_single_line_blocks = true
2525

2626
# Don't allow single line if/else blocks without braces.

examples/CallHoldAndTransfer/Program.cs

+38-57
Original file line numberDiff line numberDiff line change
@@ -58,10 +58,10 @@ static void Main()
5858
var sipTransport = new SIPTransport();
5959
sipTransport.AddSIPChannel(new SIPUDPChannel(new IPEndPoint(IPAddress.Any, SIP_LISTEN_PORT)));
6060

61-
EnableTraceLogs(sipTransport);
61+
//EnableTraceLogs(sipTransport);
6262

63-
// Initialise an RTP session to receive the RTP packets from the remote SIP server.
64-
var rtpSession = new RTPSession((int)SDPMediaFormatsEnum.PCMU, null, null, true);
63+
// Get the default speaker.
64+
var (audioOutEvent, audioOutProvider) = GetAudioOutputDevice();
6565

6666
// Create a client/server user agent to place a call to a remote SIP server along with event handlers for the different stages of the call.
6767
var userAgent = new SIPUserAgent(sipTransport, null);
@@ -79,17 +79,13 @@ static void Main()
7979
if (resp.Status == SIPResponseStatusCodesEnum.Ok)
8080
{
8181
Log.LogInformation($"{uac.CallDescriptor.To} Answered: {resp.StatusCode} {resp.ReasonPhrase}.");
82-
83-
// Only set the remote RTP end point if there hasn't already been a packet received on it.
84-
if (rtpSession.DestinationEndPoint == null)
85-
{
86-
rtpSession.DestinationEndPoint = SDP.GetSDPRTPEndPoint(resp.Body);
87-
Log.LogDebug($"Remote RTP socket {rtpSession.DestinationEndPoint}.");
88-
}
82+
PlayRemoteMedia(userAgent.RtpSession, audioOutProvider);
8983
}
9084
else
9185
{
9286
Log.LogWarning($"{uac.CallDescriptor.To} Answered: {resp.StatusCode} {resp.ReasonPhrase}.");
87+
hasCallFailed = true;
88+
exitCts.Cancel();
9389
}
9490
};
9591
userAgent.OnCallHungup += () =>
@@ -100,19 +96,6 @@ static void Main()
10096
userAgent.ServerCallCancelled += (uas) => Log.LogInformation("Incoming call cancelled by caller.");
10197
userAgent.RemotePutOnHold += () => Log.LogInformation("Remote call party has placed us on hold.");
10298
userAgent.RemoteTookOffHold += () => Log.LogInformation("Remote call party took us off hold.");
103-
userAgent.OnReinviteRequest += (uasInviteTx) =>
104-
{
105-
Log.LogDebug("Reinvite request received.");
106-
107-
// Re-INVITEs can also be changing the RTP end point. We can update this each time.
108-
IPEndPoint dstRtpEndPoint = SDP.GetSDPRTPEndPoint(uasInviteTx.TransactionRequest.Body);
109-
if(rtpSession.DestinationEndPoint != dstRtpEndPoint)
110-
{
111-
Log.LogDebug($"Remote call party RTP end point changed from {rtpSession.DestinationEndPoint} to {dstRtpEndPoint}.");
112-
rtpSession.DestinationEndPoint = dstRtpEndPoint;
113-
}
114-
uasInviteTx.SendFinalResponse(uasInviteTx.GetOkResponse(SDP.SDP_MIME_CONTENTTYPE, userAgent.Dialogue.SDP));
115-
};
11699

117100
sipTransport.SIPTransportRequestReceived += (locelEndPoint, remoteEndPoint, sipRequest) =>
118101
{
@@ -137,12 +120,13 @@ static void Main()
137120
{
138121
Log.LogInformation($"Incoming call request from {remoteEndPoint}: {sipRequest.StatusLine}.");
139122
var incomingCall = userAgent.AcceptCall(sipRequest);
123+
userAgent.Answer(incomingCall);
140124

141-
SDP remoteSDP = SDP.ParseSDPDescription(sipRequest.Body);
142-
IPAddress localIPAddress = NetServices.GetLocalAddressForRemote(IPAddress.Parse(remoteSDP.Connection.ConnectionAddress));
143-
var sdpOffer = rtpSession.GetSDP(localIPAddress);
125+
PlayRemoteMedia(userAgent.RtpSession, audioOutProvider);
144126

145-
userAgent.Answer(incomingCall, sdpOffer);
127+
string caller = sipRequest.Header.From.FromURI.ToString();
128+
caller = (string.IsNullOrEmpty(sipRequest.Header.From.FromName)) ? caller : sipRequest.Header.From.FromName + " " + caller;
129+
Log.LogInformation($"Answered incoming call from {caller} at {remoteEndPoint}.");
146130
}
147131
}
148132
else
@@ -153,18 +137,6 @@ static void Main()
153137
}
154138
};
155139

156-
// Wire up the RTP receive session to the default speaker.
157-
var (audioOutEvent, audioOutProvider) = GetAudioOutputDevice();
158-
rtpSession.OnReceivedSampleReady += (sample) =>
159-
{
160-
for (int index = 0; index < sample.Length; index++)
161-
{
162-
short pcm = NAudio.Codecs.MuLawDecoder.MuLawToLinearSample(sample[index]);
163-
byte[] pcmSample = new byte[] { (byte)(pcm & 0xFF), (byte)(pcm >> 8) };
164-
audioOutProvider.AddSamples(pcmSample, 0, 2);
165-
}
166-
};
167-
168140
// Wire up the RTP send session to the audio output device.
169141
WaveInEvent waveInEvent = GetAudioInputDevice();
170142
uint rtpSendTimestamp = 0;
@@ -179,9 +151,9 @@ static void Main()
179151
sample[sampleIndex++] = ulawByte;
180152
}
181153

182-
if (rtpSession.DestinationEndPoint != null)
154+
if (userAgent?.RtpSession?.DestinationEndPoint != null)
183155
{
184-
rtpSession.SendAudioFrame(rtpSendTimestamp, sample);
156+
userAgent.RtpSession.SendAudioFrame(rtpSendTimestamp, sample);
185157
rtpSendTimestamp += (uint)(8000 / waveInEvent.BufferMilliseconds);
186158
}
187159

@@ -201,8 +173,7 @@ static void Main()
201173
{
202174
if (!userAgent.IsCallActive)
203175
{
204-
SIPURI callUri = SIPURI.ParseSIPURI(DEFAULT_DESTINATION_SIP_URI);
205-
var callDescriptor = GetCallDescriptor(callUri, rtpSession);
176+
var callDescriptor = GetCallDescriptor(DEFAULT_DESTINATION_SIP_URI);
206177
userAgent.Call(callDescriptor);
207178
}
208179
else
@@ -281,7 +252,7 @@ static void Main()
281252

282253
Log.LogInformation("Exiting...");
283254

284-
rtpSession?.Close();
255+
userAgent?.RtpSession?.Close();
285256
waveInEvent?.StopRecording();
286257
audioOutEvent?.Stop();
287258

@@ -320,32 +291,42 @@ static void Main()
320291
/// <param name="callUri">The URI to place the call to.</param>
321292
/// <param name="rtpSession">The RTP session that will be handling the RTP/RTCP packets for the call.</param>
322293
/// <returns>A call descriptor.</returns>
323-
private static SIPCallDescriptor GetCallDescriptor(SIPURI callUri, RTPSession rtpSession)
294+
private static SIPCallDescriptor GetCallDescriptor(string callUri)
324295
{
325-
var lookupResult = SIPDNSManager.ResolveSIPService(callUri, false);
326-
Log.LogDebug($"DNS lookup result for {callUri}: {lookupResult?.GetSIPEndPoint()}.");
327-
var dstAddress = lookupResult.GetSIPEndPoint().Address;
328-
329-
IPAddress localIPAddress = NetServices.GetLocalAddressForRemote(dstAddress);
330-
331-
var sdpOffer = rtpSession.GetSDP(localIPAddress);
332-
333-
// Start the thread that places the call.
296+
// Create a call descriptor to place an outgoing call.
334297
SIPCallDescriptor callDescriptor = new SIPCallDescriptor(
335298
SIP_USERNAME,
336299
SIP_PASSWORD,
337-
callUri.ToString(),
300+
callUri,
338301
$"sip:{SIP_USERNAME}@localhost",
339-
callUri.CanonicalAddress,
302+
callUri,
340303
null, null, null,
341304
SIPCallDirection.Out,
342305
SDP.SDP_MIME_CONTENTTYPE,
343-
sdpOffer.ToString(),
306+
null,
344307
null);
345308

346309
return callDescriptor;
347310
}
348311

312+
/// <summary>
313+
/// Wires up the active RTP session to the speaker.
314+
/// </summary>
315+
/// <param name="rtpSession">The active RTP session receiving the remote party's RTP packets.</param>
316+
/// <param name="audioOutProvider">The audio buffer for the default system audio output device.</param>
317+
private static void PlayRemoteMedia(RTPSession rtpSession, BufferedWaveProvider audioOutProvider)
318+
{
319+
rtpSession.OnReceivedSampleReady += (sample) =>
320+
{
321+
for (int index = 0; index < sample.Length; index++)
322+
{
323+
short pcm = NAudio.Codecs.MuLawDecoder.MuLawToLinearSample(sample[index]);
324+
byte[] pcmSample = new byte[] { (byte)(pcm & 0xFF), (byte)(pcm >> 8) };
325+
audioOutProvider.AddSamples(pcmSample, 0, 2);
326+
}
327+
};
328+
}
329+
349330
/// <summary>
350331
/// Get the audio output device, e.g. speaker.
351332
/// Note that NAudio.Wave.WaveOut is not available for .Net Standard so no easy way to check if

examples/Softphone/SIPSorcery.SoftPhone/Media/AudioChannel.cs

+4-2
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
// Filename: AudioChannel.cs
33
//
44
// Description: This class manages the coding and decoding of audio from physical
5-
// devices into and for RTP packets.
5+
// devices and playback from samples recevied from RTP packets.
66
//
77
// Author(s):
88
// Aaron Clauson ([email protected])
@@ -26,6 +26,8 @@ namespace SIPSorcery.SoftPhone
2626
{
2727
public class AudioChannel
2828
{
29+
public const int AUDIO_INPUT_BUFFER_MILLISECONDS = 80;
30+
2931
private ILog logger = AppState.logger;
3032

3133
private BufferedWaveProvider m_waveProvider;
@@ -57,7 +59,7 @@ public AudioChannel()
5759
else
5860
{
5961
m_waveInEvent = new WaveInEvent();
60-
m_waveInEvent.BufferMilliseconds = 20;
62+
m_waveInEvent.BufferMilliseconds = AUDIO_INPUT_BUFFER_MILLISECONDS;
6163
m_waveInEvent.NumberOfBuffers = 1;
6264
m_waveInEvent.DeviceNumber = 0;
6365
m_waveInEvent.DataAvailable += AudioSampleAvailable;

0 commit comments

Comments
 (0)