1010using System . IO ;
1111using System . Linq ;
1212using System . Net ;
13+ using System . Net . Security ;
1314using System . Text ;
1415
1516namespace SharpRTSPClient
@@ -42,6 +43,7 @@ public class RTSPClient : IDisposable
4243 public event EventHandler < NewStreamEventArgs > NewAudioStream ;
4344 public event EventHandler < SimpleDataEventArgs > ReceivedVideoData ;
4445 public event EventHandler < SimpleDataEventArgs > ReceivedAudioData ;
46+ public event EventHandler < EventArgs > Stopped ;
4547
4648 public bool ProcessRTCP { get ; set ; } = true ; // answer RTCP
4749 public event EventHandler < RawRtcpDataEventArgs > ReceivedRawVideoRTCP ;
@@ -65,13 +67,13 @@ private enum RtspStatus { WaitingToConnect, Connecting, ConnectFailed, Connected
6567 private IRtpTransport _videoRtpTransport ;
6668 private IRtpTransport _audioRtpTransport ;
6769
68- private Uri _uri ; // RTSP URI (username & password will be stripped out)
70+ private Uri _uri = null ; // RTSP URI (username & password will be stripped out)
6971 private string _session = "" ; // RTSP Session
7072 private Authentication _authentication ;
7173 private NetworkCredential _credentials = new NetworkCredential ( ) ;
72- private bool _clientWantsVideo = false ; // Client wants to receive Video
73- private bool _clientWantsAudio = false ; // Client wants to receive Audio
74-
74+ private MediaRequest _mediaRequest = MediaRequest . VIDEO_AND_AUDIO ;
75+ private RemoteCertificateValidationCallback _userCertificateSelectionCallback = null ;
76+ private bool _autoReconnect = false ;
7577 private Uri _videoUri = null ; // URI used for the Video Track
7678 private int _videoPayload = - 1 ; // Payload Type for the Video. (often 96 which is the first dynamic payload value. Bosch use 35)
7779
@@ -148,9 +150,21 @@ public RTSPClient(ILoggerFactory loggerFactory)
148150 /// <param name="mediaRequest">Media request type <see cref="MediaRequest>."/></param>
149151 /// <param name="playbackSession">Playback session.</param>
150152 /// <param name="userCertificateSelectionCallback">Callback for user certificate selection.</param>
151- public void Connect ( string url , RTPTransport rtpTransport , string username = null , string password = null , MediaRequest mediaRequest = MediaRequest . VIDEO_AND_AUDIO , bool playbackSession = false , System . Net . Security . RemoteCertificateValidationCallback userCertificateSelectionCallback = null )
153+ /// <param name="autoReconnect">Automatically try to reconnect after losing the connection.</param>
154+ public void Connect (
155+ string url ,
156+ RTPTransport rtpTransport ,
157+ string username = null ,
158+ string password = null ,
159+ MediaRequest mediaRequest = MediaRequest . VIDEO_AND_AUDIO ,
160+ bool playbackSession = false ,
161+ RemoteCertificateValidationCallback userCertificateSelectionCallback = null ,
162+ bool autoReconnect = false )
152163 {
153- Connect ( new Uri ( url ) , rtpTransport , username , password , mediaRequest , playbackSession , userCertificateSelectionCallback ) ;
164+ if ( string . IsNullOrEmpty ( url ) )
165+ throw new ArgumentNullException ( nameof ( url ) ) ;
166+
167+ Connect ( new Uri ( url ) , rtpTransport , username , password , mediaRequest , playbackSession , userCertificateSelectionCallback , autoReconnect ) ;
154168 }
155169
156170 /// <summary>
@@ -163,40 +177,78 @@ public void Connect(string url, RTPTransport rtpTransport, string username = nul
163177 /// <param name="mediaRequest">Media request type <see cref="MediaRequest>."/></param>
164178 /// <param name="playbackSession">Playback session.</param>
165179 /// <param name="userCertificateSelectionCallback">Callback for user certificate selection.</param>
166- public void Connect ( Uri uri , RTPTransport rtpTransport , string username , string password , MediaRequest mediaRequest , bool playbackSession , System . Net . Security . RemoteCertificateValidationCallback userCertificateSelectionCallback )
180+ /// <param name="autoReconnect">Automatically try to reconnect after losing the connection.</param>
181+ public void Connect (
182+ Uri uri ,
183+ RTPTransport rtpTransport ,
184+ string username = null ,
185+ string password = null ,
186+ MediaRequest mediaRequest = MediaRequest . VIDEO_AND_AUDIO ,
187+ bool playbackSession = false ,
188+ RemoteCertificateValidationCallback userCertificateSelectionCallback = null ,
189+ bool autoReconnect = false )
167190 {
168- _logger . LogDebug ( "Connecting to {url} " , uri ) ;
169-
170- _uri = uri ;
171-
172- _playbackSession = playbackSession ;
191+ if ( uri == null )
192+ throw new ArgumentNullException ( nameof ( uri ) ) ;
173193
174194 // Use URI to extract username and password and to make a new URL without the username and password
175- var hostname = _uri . Host ;
176- var port = _uri . Port ;
177- try
195+ var hostname = uri . Host ;
196+ var port = uri . Port ;
197+ NetworkCredential credentials = null ;
198+
199+ if ( uri . UserInfo . Length > 0 )
178200 {
179- if ( _uri . UserInfo . Length > 0 )
180- {
181- _credentials = new NetworkCredential ( _uri . UserInfo . Split ( ':' ) [ 0 ] , _uri . UserInfo . Split ( ':' ) [ 1 ] ) ;
182- _uri = new Uri ( _uri . GetComponents ( UriComponents . AbsoluteUri & ~ UriComponents . UserInfo , UriFormat . UriEscaped ) ) ;
183- }
184- else
185- {
186- _credentials = new NetworkCredential ( username , password ) ;
187- }
201+ credentials = new NetworkCredential ( uri . UserInfo . Split ( ':' ) [ 0 ] , uri . UserInfo . Split ( ':' ) [ 1 ] ) ;
202+ uri = new Uri ( uri . GetComponents ( UriComponents . AbsoluteUri & ~ UriComponents . UserInfo , UriFormat . UriEscaped ) ) ;
188203 }
189- catch
204+ else
190205 {
191- _credentials = new NetworkCredential ( ) ;
206+ credentials = new NetworkCredential ( username , password ) ;
192207 }
193208
209+ Connect ( uri , rtpTransport , credentials , mediaRequest , playbackSession , userCertificateSelectionCallback , autoReconnect ) ;
210+ }
211+
212+ /// <summary>
213+ /// Connects to the specified RTSP server.
214+ /// </summary>
215+ /// <param name="uri">The URI of the RTSP server.</param>
216+ /// <param name="rtpTransport">Type of the RTP transport <see cref="RTPTransport"/>.</param>
217+ /// <param name="credentials">Network credentials.</param>
218+ /// <param name="mediaRequest">Media request type <see cref="MediaRequest>."/></param>
219+ /// <param name="playbackSession">Playback session.</param>
220+ /// <param name="userCertificateSelectionCallback">Callback for user certificate selection.</param>
221+ /// <param name="autoReconnect">Automatically try to reconnect after losing the connection.</param>
222+ public void Connect (
223+ Uri uri ,
224+ RTPTransport rtpTransport ,
225+ NetworkCredential credentials = null ,
226+ MediaRequest mediaRequest = MediaRequest . VIDEO_AND_AUDIO ,
227+ bool playbackSession = false ,
228+ RemoteCertificateValidationCallback userCertificateSelectionCallback = null ,
229+ bool autoReconnect = false )
230+ {
231+ if ( _rtspClient != null )
232+ throw new InvalidOperationException ( "You must first call Stop() before re-connecting!" ) ;
233+
234+ _logger . LogDebug ( "Connecting to {url} " , uri ) ;
235+
236+ this . _uri = uri ;
237+ // Check the RTP Transport
238+ // If the RTP transport is TCP then we interleave the RTP packets in the RTSP stream
239+ // If the RTP transport is UDP, we initialise two UDP sockets (one for video, one for RTCP status messages)
240+ // If the RTP transport is MULTICAST, we have to wait for the SETUP message to get the Multicast Address from the RTSP server
241+ this . _rtpTransport = rtpTransport ;
242+ this . _credentials = credentials ?? new NetworkCredential ( ) ;
194243 // We can ask the RTSP server for Video, Audio or both. If we don't want audio we don't need to SETUP the audio channel or receive it
195- _clientWantsVideo = ( mediaRequest is MediaRequest . VIDEO_ONLY || mediaRequest is MediaRequest . VIDEO_AND_AUDIO ) ;
196- _clientWantsAudio = ( mediaRequest is MediaRequest . AUDIO_ONLY || mediaRequest is MediaRequest . VIDEO_AND_AUDIO ) ;
244+ this . _mediaRequest = mediaRequest ;
245+ this . _playbackSession = playbackSession ;
246+ this . _userCertificateSelectionCallback = userCertificateSelectionCallback ;
247+ this . _autoReconnect = autoReconnect ;
197248
198249 // Connect to a RTSP Server. The RTSP session is a TCP connection
199250 _rtspSocketStatus = RtspStatus . Connecting ;
251+
200252 try
201253 {
202254 switch ( _uri . Scheme )
@@ -210,7 +262,7 @@ public void Connect(Uri uri, RTPTransport rtpTransport, string username, string
210262
211263 default :
212264 {
213- _rtspSocket = Rtsp . RtspUtils . CreateRtspTransportFromUrl ( _uri , userCertificateSelectionCallback ) ;
265+ _rtspSocket = Rtsp . RtspUtils . CreateRtspTransportFromUrl ( _uri , _userCertificateSelectionCallback ) ;
214266 }
215267 break ;
216268 }
@@ -219,13 +271,15 @@ public void Connect(Uri uri, RTPTransport rtpTransport, string username, string
219271 {
220272 _rtspSocketStatus = RtspStatus . ConnectFailed ;
221273 _logger . LogWarning ( "Error - did not connect" ) ;
274+ Stopped ? . Invoke ( this , EventArgs . Empty ) ;
222275 return ;
223276 }
224277
225278 if ( ! _rtspSocket . Connected )
226279 {
227280 _rtspSocketStatus = RtspStatus . ConnectFailed ;
228281 _logger . LogWarning ( "Error - did not connect" ) ;
282+ Stopped ? . Invoke ( this , EventArgs . Empty ) ;
229283 return ;
230284 }
231285
@@ -234,22 +288,16 @@ public void Connect(Uri uri, RTPTransport rtpTransport, string username, string
234288 // Connect a RTSP Listener to the RTSP Socket (or other Stream) to send RTSP messages and listen for RTSP replies
235289 _rtspClient = new RtspListener ( _rtspSocket , _loggerFactory . CreateLogger < RtspListener > ( ) )
236290 {
237- AutoReconnect = false
291+ AutoReconnect = _autoReconnect
238292 } ;
239293
240294 _rtspClient . MessageReceived += RtspMessageReceived ;
241295 _rtspClient . Start ( ) ; // start listening for messages from the server (messages fire the MessageReceived event)
242296
243- // Check the RTP Transport
244- // If the RTP transport is TCP then we interleave the RTP packets in the RTSP stream
245- // If the RTP transport is UDP, we initialise two UDP sockets (one for video, one for RTCP status messages)
246- // If the RTP transport is MULTICAST, we have to wait for the SETUP message to get the Multicast Address from the RTSP server
247- this . _rtpTransport = rtpTransport ;
248-
249297 if ( rtpTransport == RTPTransport . UDP )
250298 {
251299 // give a range of 500 pairs (1000 addresses) to try incase some address are in use
252- _videoRtpTransport = new UDPSocket ( 50000 , 51000 ) ;
300+ _videoRtpTransport = new UDPSocket ( 50000 , 51000 ) ;
253301 _audioRtpTransport = new UDPSocket ( 50000 , 51000 ) ;
254302 }
255303
@@ -285,6 +333,18 @@ public void Connect(Uri uri, RTPTransport rtpTransport, string username, string
285333 _rtspClient . SendMessage ( optionsMessage ) ;
286334 }
287335
336+ /// <summary>
337+ /// Attempt to reconnect when a connection to the server is lost.
338+ /// </summary>
339+ /// <exception cref="InvalidOperationException">Reconnect can only be called after calling Connect.</exception>
340+ public void TryReconnect ( )
341+ {
342+ if ( _uri == null )
343+ throw new InvalidOperationException ( "You must first call Connect() before re-connecting!" ) ;
344+
345+ Connect ( _uri , _rtpTransport , _credentials , _mediaRequest , _playbackSession , _userCertificateSelectionCallback , _autoReconnect ) ;
346+ }
347+
288348 /// <summary>
289349 /// Returns true if this connection failed, or if it connected but is no longer connected.
290350 /// </summary>
@@ -398,6 +458,11 @@ public void Play(DateTime seekTimeFrom, DateTime seekTimeTo, double speed = 1.0)
398458 /// Stop playing.
399459 /// </summary>
400460 public void Stop ( )
461+ {
462+ StopClient ( ) ;
463+ }
464+
465+ private void StopClient ( )
401466 {
402467 // Send TEARDOWN
403468 RtspRequest teardown_message = new RtspRequestTeardown
@@ -409,14 +474,43 @@ public void Stop()
409474 _rtspClient ? . SendMessage ( teardown_message ) ;
410475
411476 // Stop the keepalive timer
412- _keepaliveTimer ? . Stop ( ) ;
477+ var keepaliveTimer = _keepaliveTimer ;
478+ if ( keepaliveTimer != null )
479+ {
480+ keepaliveTimer . Elapsed -= SendKeepAlive ;
481+ keepaliveTimer . Dispose ( ) ;
482+ _keepaliveTimer = null ;
483+ }
413484
414485 // clear up any UDP sockets
415- _videoRtpTransport ? . Stop ( ) ;
416- _audioRtpTransport ? . Stop ( ) ;
486+ var videoRtpTransport = _videoRtpTransport ;
487+ if ( videoRtpTransport != null )
488+ {
489+ videoRtpTransport . Stop ( ) ;
490+ videoRtpTransport . DataReceived -= VideoRtpDataReceived ;
491+ videoRtpTransport . ControlReceived -= VideoRtcpControlDataReceived ;
492+ _videoRtpTransport = null ;
493+ }
494+
495+ var audioRtpTransport = _audioRtpTransport ;
496+ if ( audioRtpTransport != null )
497+ {
498+ audioRtpTransport . Stop ( ) ;
499+ audioRtpTransport . DataReceived -= AudioRtpDataReceived ;
500+ audioRtpTransport . ControlReceived -= AudioRtcpControlDataReceived ;
501+ _audioRtpTransport = null ;
502+ }
417503
418504 // Drop the RTSP session
419- _rtspClient ? . Stop ( ) ;
505+ var rtspClient = _rtspClient ;
506+ if ( rtspClient != null )
507+ {
508+ rtspClient . MessageReceived -= RtspMessageReceived ;
509+ rtspClient . Stop ( ) ;
510+ _rtspClient = null ;
511+ }
512+
513+ _rtspSocket = null ; // closed by rtspClient.Stop()
420514 }
421515
422516 /// <summary>
@@ -705,8 +799,9 @@ private void RtspMessageReceived(object sender, RtspChunkEventArgs e)
705799
706800 if ( message . ReturnCode == 401 && message . OriginalRequest ? . Headers . ContainsKey ( RtspHeaderNames . Authorization ) == true )
707801 {
708- _logger . LogError ( "Fail to authenticate stoping here" ) ;
709- Stop ( ) ;
802+ _logger . LogError ( "Fail to authenticate stopping here" ) ;
803+ StopClient ( ) ;
804+ Stopped ? . Invoke ( this , EventArgs . Empty ) ;
710805 return ;
711806 }
712807
@@ -911,7 +1006,7 @@ private void HandleDescribeResponse(RtspResponse message)
9111006
9121007 // Process each 'Media' Attribute in the SDP (each sub-stream)
9131008 // to look for first supported video substream
914- if ( _clientWantsVideo )
1009+ if ( _mediaRequest is MediaRequest . VIDEO_ONLY || _mediaRequest is MediaRequest . VIDEO_AND_AUDIO )
9151010 {
9161011 foreach ( Media media in sdpData . Medias . Where ( m => m . MediaType == Media . MediaTypes . video ) )
9171012 {
@@ -1057,7 +1152,7 @@ private void HandleDescribeResponse(RtspResponse message)
10571152 }
10581153 }
10591154
1060- if ( _clientWantsAudio )
1155+ if ( _mediaRequest is MediaRequest . AUDIO_ONLY || _mediaRequest is MediaRequest . VIDEO_AND_AUDIO )
10611156 {
10621157 foreach ( Media media in sdpData . Medias . Where ( m => m . MediaType == Media . MediaTypes . audio ) )
10631158 {
@@ -1262,11 +1357,7 @@ protected virtual void Dispose(bool disposing)
12621357 {
12631358 if ( disposing )
12641359 {
1265- Stop ( ) ;
1266-
1267- _rtspClient ? . Dispose ( ) ;
1268- _videoRtpTransport ? . Dispose ( ) ;
1269- _audioRtpTransport ? . Dispose ( ) ;
1360+ StopClient ( ) ;
12701361 }
12711362
12721363 _disposedValue = true ;
0 commit comments