@@ -64,6 +64,10 @@ public SendSilenceJob(Timer timer, SIPUserAgent ua)
6464
6565 class Program
6666 {
67+ private const string PUBLIC_IP_ADDRESS_ENV_VAR = "PUBLIC_IP_ADDRESS" ;
68+ private const string RTP_PORT_ENV_VAR = "RTP_PORT" ;
69+ private const string IS_HEADLESS_ENV_VAR = "IS_HEADLESS" ;
70+
6771 //private static string DEFAULT_CALL_DESTINATION = "sip:*61@192.168.0.48";
6872 private static string DEFAULT_CALL_DESTINATION = "sip:aaron@127.0.0.1:7060;transport=tcp" ;
6973 private static string DEFAULT_TRANSFER_DESTINATION = "sip:*61@192.168.0.48" ;
@@ -93,38 +97,85 @@ class Program
9397 /// </summary>
9498 private static ConcurrentDictionary < string , SIPRegistrationUserAgent > _registrations = new ConcurrentDictionary < string , SIPRegistrationUserAgent > ( ) ;
9599
100+ private static string _publicIPAddress = null ;
101+ private static int _rtpPort = 0 ;
102+
103+ /// <summary>
104+ /// If running on K8S or similar there is no console window to receive key presses.
105+ /// </summary>
106+ private static bool _isHeadless = false ;
107+
96108 static async Task Main ( )
97109 {
98110 Console . WriteLine ( "SIPSorcery SIP Call Server example." ) ;
99- Console . WriteLine ( "Press 'c' to place a call to the default destination." ) ;
100- Console . WriteLine ( "Press 'd' to send a random DTMF tone to the newest call." ) ;
101- Console . WriteLine ( "Press 'h' to hangup the oldest call." ) ;
102- Console . WriteLine ( "Press 'H' to hangup all calls." ) ;
103- Console . WriteLine ( "Press 'l' to list current calls." ) ;
104- Console . WriteLine ( "Press 'r' to list current registrations." ) ;
105- Console . WriteLine ( "Press 't' to transfer the newest call to the default destination." ) ;
106- Console . WriteLine ( "Press 'q' to quit." ) ;
111+
112+ if ( ! _isHeadless )
113+ {
114+ Console . WriteLine ( "Press 'c' to place a call to the default destination." ) ;
115+ Console . WriteLine ( "Press 'd' to send a random DTMF tone to the newest call." ) ;
116+ Console . WriteLine ( "Press 'h' to hangup the oldest call." ) ;
117+ Console . WriteLine ( "Press 'H' to hangup all calls." ) ;
118+ Console . WriteLine ( "Press 'l' to list current calls." ) ;
119+ Console . WriteLine ( "Press 'r' to list current registrations." ) ;
120+ Console . WriteLine ( "Press 't' to transfer the newest call to the default destination." ) ;
121+ Console . WriteLine ( "Press 'q' to quit." ) ;
122+ }
107123
108124 Log = AddConsoleLogger ( ) ;
109125
110126 // Set up a default SIP transport.
111127 _sipTransport = new SIPTransport ( ) ;
128+
129+ if ( ! string . IsNullOrWhiteSpace ( Environment . GetEnvironmentVariable ( PUBLIC_IP_ADDRESS_ENV_VAR ) ) )
130+ {
131+ _publicIPAddress = Environment . GetEnvironmentVariable ( PUBLIC_IP_ADDRESS_ENV_VAR ) ;
132+ _sipTransport . ContactHost = _publicIPAddress ;
133+ }
134+
135+ if ( ! string . IsNullOrWhiteSpace ( Environment . GetEnvironmentVariable ( RTP_PORT_ENV_VAR ) ) )
136+ {
137+ int . TryParse ( Environment . GetEnvironmentVariable ( RTP_PORT_ENV_VAR ) , out _rtpPort ) ;
138+ }
139+
140+ if ( ! string . IsNullOrWhiteSpace ( Environment . GetEnvironmentVariable ( IS_HEADLESS_ENV_VAR ) ) )
141+ {
142+ bool . TryParse ( Environment . GetEnvironmentVariable ( IS_HEADLESS_ENV_VAR ) , out _isHeadless ) ;
143+ }
144+
112145 _sipTransport . AddSIPChannel ( new SIPUDPChannel ( new IPEndPoint ( IPAddress . Any , SIP_LISTEN_PORT ) ) ) ;
113146 _sipTransport . AddSIPChannel ( new SIPUDPChannel ( new IPEndPoint ( IPAddress . IPv6Any , SIP_LISTEN_PORT ) ) ) ;
114147 _sipTransport . AddSIPChannel ( new SIPTCPChannel ( new IPEndPoint ( IPAddress . Any , SIP_LISTEN_PORT ) ) ) ;
115148 var localhostCertificate = new X509Certificate2 ( SIPS_CERTIFICATE_PATH ) ;
116149 _sipTransport . AddSIPChannel ( new SIPTLSChannel ( localhostCertificate , new IPEndPoint ( IPAddress . Any , SIPS_LISTEN_PORT ) ) ) ;
117150 // If it's desired to listen on a single IP address use the equivalent of:
118151 //_sipTransport.AddSIPChannel(new SIPUDPChannel(new IPEndPoint(IPAddress.Parse("192.168.11.50"), SIP_LISTEN_PORT)));
119- _sipTransport . EnableTraceLogs ( ) ;
152+ // _sipTransport.EnableTraceLogs();
120153
121154 _sipTransport . SIPTransportRequestReceived += OnRequest ;
122155
123156 // Uncomment to enable registrations.
124157 //StartRegistrations(_sipTransport, _sipAccounts);
125158
126- CancellationTokenSource exitCts = new CancellationTokenSource ( ) ;
127- await Task . Run ( ( ) => OnKeyPress ( exitCts . Token ) ) ;
159+ if ( ! _isHeadless )
160+ {
161+ CancellationTokenSource exitCts = new CancellationTokenSource ( ) ;
162+ await Task . Run ( ( ) => OnKeyPress ( exitCts . Token ) ) ;
163+
164+ Console . WriteLine ( "Press ctrl-c to exit." ) ;
165+ }
166+ else
167+ {
168+ // Ctrl-c will gracefully exit the call at any point.
169+ ManualResetEvent exitMre = new ManualResetEvent ( false ) ;
170+ Console . CancelKeyPress += delegate ( object sender , ConsoleCancelEventArgs e )
171+ {
172+ e . Cancel = true ;
173+ exitMre . Set ( ) ;
174+ } ;
175+
176+ // Wait for a signal saying the call failed, was cancelled with ctrl-c or completed.
177+ exitMre . WaitOne ( ) ;
178+ }
128179
129180 Log . LogInformation ( "Exiting..." ) ;
130181
@@ -159,7 +210,7 @@ private static async Task OnKeyPress(CancellationToken exit)
159210 ua . OnRtpEvent += ( evt , hdr ) => Log . LogDebug ( $ "rtp event { evt . EventID } , duration { evt . Duration } , end of event { evt . EndOfEvent } , timestamp { hdr . Timestamp } , marker { hdr . MarkerBit } .") ;
160211 ua . OnCallHungup += OnHangup ;
161212
162- var rtpSession = CreateRtpSession ( ua , null ) ;
213+ var rtpSession = CreateRtpSession ( ua , null , _rtpPort ) ;
163214 var callResult = await ua . Call ( DEFAULT_CALL_DESTINATION , null , null , rtpSession ) ;
164215
165216 if ( callResult )
@@ -297,7 +348,7 @@ private static async Task OnKeyPress(CancellationToken exit)
297348 /// <param name="dst">THe destination specified on an incoming call. Can be used to
298349 /// set the audio source.</param>
299350 /// <returns>A new RTP session object.</returns>
300- private static VoIPMediaSession CreateRtpSession ( SIPUserAgent ua , string dst )
351+ private static VoIPMediaSession CreateRtpSession ( SIPUserAgent ua , string dst , int bindPort )
301352 {
302353 List < AudioCodecsEnum > codecs = new List < AudioCodecsEnum > { AudioCodecsEnum . PCMU , AudioCodecsEnum . PCMA , AudioCodecsEnum . G722 } ;
303354
@@ -310,12 +361,12 @@ private static VoIPMediaSession CreateRtpSession(SIPUserAgent ua, string dst)
310361 Log . LogInformation ( $ "RTP audio session source set to { audioSource } .") ;
311362
312363 AudioExtrasSource audioExtrasSource = new AudioExtrasSource ( new AudioEncoder ( ) , new AudioSourceOptions { AudioSource = audioSource } ) ;
313- audioExtrasSource . RestrictFormats ( formats => codecs . Contains ( formats . Codec ) ) ;
314- var rtpAudioSession = new VoIPMediaSession ( new MediaEndPoints { AudioSource = audioExtrasSource } ) ;
364+ audioExtrasSource . RestrictFormats ( formats => codecs . Contains ( formats . Codec ) ) ;
365+ var rtpAudioSession = new VoIPMediaSession ( new MediaEndPoints { AudioSource = audioExtrasSource } , bindPort : bindPort ) ;
315366 rtpAudioSession . AcceptRtpFromAny = true ;
316367
317368 // Wire up the event handler for RTP packets received from the remote party.
318- rtpAudioSession . OnRtpPacketReceived += ( ep , type , rtp ) => OnRtpPacketReceived ( ua , type , rtp ) ;
369+ rtpAudioSession . OnRtpPacketReceived += ( ep , type , rtp ) => OnRtpPacketReceived ( ua , ep , type , rtp ) ;
319370 rtpAudioSession . OnTimeout += ( mediaType ) =>
320371 {
321372 if ( ua ? . Dialogue != null )
@@ -339,9 +390,10 @@ private static VoIPMediaSession CreateRtpSession(SIPUserAgent ua, string dst)
339390 /// <param name="ua">The SIP user agent associated with the RTP session.</param>
340391 /// <param name="type">The media type of the RTP packet (audio or video).</param>
341392 /// <param name="rtpPacket">The RTP packet received from the remote party.</param>
342- private static void OnRtpPacketReceived ( SIPUserAgent ua , SDPMediaTypesEnum type , RTPPacket rtpPacket )
393+ private static void OnRtpPacketReceived ( SIPUserAgent ua , IPEndPoint remoteEp , SDPMediaTypesEnum type , RTPPacket rtpPacket )
343394 {
344395 // The raw audio data is available in rtpPacket.Payload.
396+ //Log.LogTrace($"OnRtpPacketReceived from {remoteEp}.");
345397 }
346398
347399 /// <summary>
@@ -386,14 +438,26 @@ private static async Task OnRequest(SIPEndPoint localSIPEndPoint, SIPEndPoint re
386438 ua . Hangup ( ) ;
387439 } ;
388440
441+ //bool wasMangled = false;
442+ //sipRequest.Body = SIPPacketMangler.MangleSDP(sipRequest.Body, remoteEndPoint.Address.ToString(), out wasMangled);
443+ //Log.LogDebug("INVITE was mangled=" + wasMangled + " remote=" + remoteEndPoint.Address.ToString() + ".");
444+ //sipRequest.Header.ContentLength = sipRequest.Body.Length;
445+
389446 var uas = ua . AcceptCall ( sipRequest ) ;
390- var rtpSession = CreateRtpSession ( ua , sipRequest . URI . User ) ;
447+ var rtpSession = CreateRtpSession ( ua , sipRequest . URI . User , _rtpPort ) ;
391448
392449 // Insert a brief delay to allow testing of the "Ringing" progress response.
393450 // Without the delay the call gets answered before it can be sent.
394451 await Task . Delay ( 500 ) ;
395452
396- await ua . Answer ( uas , rtpSession ) ;
453+ if ( ! string . IsNullOrWhiteSpace ( _publicIPAddress ) )
454+ {
455+ await ua . Answer ( uas , rtpSession , IPAddress . Parse ( _publicIPAddress ) ) ;
456+ }
457+ else
458+ {
459+ await ua . Answer ( uas , rtpSession ) ;
460+ }
397461
398462 if ( ua . IsCallActive )
399463 {
@@ -434,7 +498,7 @@ private static void OnHangup(SIPDialogue dialogue)
434498 string callID = dialogue . CallId ;
435499 if ( _calls . ContainsKey ( callID ) )
436500 {
437- if ( _calls . TryRemove ( callID , out var ua ) )
501+ if ( _calls . TryRemove ( callID , out var ua ) )
438502 {
439503 // This app only uses each SIP user agent once so here the agent is
440504 // explicitly closed to prevent is responding to any new SIP requests.
@@ -475,7 +539,7 @@ private static Microsoft.Extensions.Logging.ILogger AddConsoleLogger()
475539 {
476540 var serilogLogger = new LoggerConfiguration ( )
477541 . Enrich . FromLogContext ( )
478- . MinimumLevel . Is ( Serilog . Events . LogEventLevel . Debug )
542+ . MinimumLevel . Is ( Serilog . Events . LogEventLevel . Verbose )
479543 . WriteTo . Console ( )
480544 . CreateLogger ( ) ;
481545 var factory = new SerilogLoggerFactory ( serilogLogger ) ;
0 commit comments