config: remove advanced from add_password()
[vlc.git] / modules / access / srt.c
1 /*****************************************************************************
2  * srt.c: SRT (Secure Reliable Transport) input module
3  *****************************************************************************
4  * Copyright (C) 2017-2018, Collabora Ltd.
5  * Copyright (C) 2018, Haivision Systems Inc.
6  *
7  * Authors: Justin Kim <justin.kim@collabora.com>
8  *          Roman Diouskine <rdiouskine@haivision.com>
9  *
10  * This program is free software; you can redistribute it and/or modify it
11  * under the terms of the GNU Lesser General Public License as published by
12  * the Free Software Foundation; either version 2.1 of the License, or
13  * (at your option) any later version.
14  *
15  * This program is distributed in the hope that it will be useful,
16  * but WITHOUT ANY WARRANTY; without even the implied warranty of
17  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18  * GNU Lesser General Public License for more details.
19  *
20  * You should have received a copy of the GNU Lesser General Public License
21  * along with this program; if not, write to the Free Software Foundation,
22  * Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
23  *****************************************************************************/
24
25 #ifdef HAVE_CONFIG_H
26 # include "config.h"
27 #endif
28
29 #include <vlc_common.h>
30 #include <vlc_interrupt.h>
31 #include <vlc_fs.h>
32 #include <vlc_plugin.h>
33 #include <vlc_access.h>
34
35 #include <vlc_network.h>
36 #include <vlc_url.h>
37
38 #include <srt/srt.h>
39
40 /* libsrt defines default packet size as 1316 internally
41  * so srt module takes same value. */
42 #define SRT_DEFAULT_CHUNK_SIZE 1316
43 /* The default timeout is -1 (infinite) */
44 #define SRT_DEFAULT_POLL_TIMEOUT -1
45 /* The default latency is 125
46  * which uses srt library internally */
47 #define SRT_DEFAULT_LATENCY 125
48 /* Crypto key length in bytes. */
49 #define SRT_KEY_LENGTH_TEXT N_("Crypto key length in bytes")
50 #define SRT_DEFAULT_KEY_LENGTH 16
51 static const int srt_key_lengths[] = {
52     16, 24, 32,
53 };
54
55 static const char *const srt_key_length_names[] = {
56     N_("16 bytes"), N_("24 bytes"), N_("32 bytes"),
57 };
58
59 struct stream_sys_t
60 {
61     SRTSOCKET   sock;
62     int         i_poll_id;
63     vlc_mutex_t lock;
64     bool        b_interrupted;
65     char       *psz_host;
66     int         i_port;
67 };
68
69 static void srt_wait_interrupted(void *p_data)
70 {
71     stream_t *p_stream = p_data;
72     stream_sys_t *p_sys = p_stream->p_sys;
73
74     vlc_mutex_lock( &p_sys->lock );
75     if ( p_sys->i_poll_id >= 0 &&  p_sys->sock != SRT_INVALID_SOCK )
76     {
77         p_sys->b_interrupted = true;
78
79         msg_Dbg( p_stream, "Waking up srt_epoll_wait");
80
81         /* Removing all socket descriptors from the monitoring list
82          * wakes up SRT's threads. We only have one to remove. */
83         srt_epoll_remove_usock( p_sys->i_poll_id, p_sys->sock );
84     }
85     vlc_mutex_unlock( &p_sys->lock );
86 }
87
88 static int Control(stream_t *p_stream, int i_query, va_list args)
89 {
90     int i_ret = VLC_SUCCESS;
91
92     switch( i_query )
93     {
94         case STREAM_CAN_SEEK:
95         case STREAM_CAN_FASTSEEK:
96         case STREAM_CAN_PAUSE:
97         case STREAM_CAN_CONTROL_PACE:
98             *va_arg( args, bool * ) = false;
99             break;
100         case STREAM_GET_PTS_DELAY:
101             *va_arg( args, int64_t * ) = INT64_C(1000)
102                    * var_InheritInteger(p_stream, "network-caching");
103             break;
104         default:
105             i_ret = VLC_EGENERIC;
106             break;
107     }
108
109     return i_ret;
110 }
111
112 static bool srt_schedule_reconnect(stream_t *p_stream)
113 {
114     int         i_latency;
115     int         stat;
116     char        *psz_passphrase = NULL;
117
118     struct addrinfo hints = {
119         .ai_socktype = SOCK_DGRAM,
120     }, *res = NULL;
121
122     stream_sys_t *p_sys = p_stream->p_sys;
123     bool failed = false;
124
125     stat = vlc_getaddrinfo( p_sys->psz_host, p_sys->i_port, &hints, &res );
126     if ( stat )
127     {
128         msg_Err( p_stream, "Cannot resolve [%s]:%d (reason: %s)",
129                  p_sys->psz_host,
130                  p_sys->i_port,
131                  gai_strerror( stat ) );
132
133         failed = true;
134         goto out;
135     }
136
137     /* Always start with a fresh socket */
138     if (p_sys->sock != SRT_INVALID_SOCK)
139     {
140         srt_epoll_remove_usock( p_sys->i_poll_id, p_sys->sock );
141         srt_close( p_sys->sock );
142     }
143
144     p_sys->sock = srt_socket( res->ai_family, SOCK_DGRAM, 0 );
145     if ( p_sys->sock == SRT_INVALID_SOCK )
146     {
147         msg_Err( p_stream, "Failed to open socket." );
148         failed = true;
149         goto out;
150     }
151
152     /* Make SRT non-blocking */
153     srt_setsockopt( p_sys->sock, 0, SRTO_SNDSYN,
154         &(bool) { false }, sizeof( bool ) );
155     srt_setsockopt( p_sys->sock, 0, SRTO_RCVSYN,
156         &(bool) { false }, sizeof( bool ) );
157
158     /* Make sure TSBPD mode is enable (SRT mode) */
159     srt_setsockopt( p_sys->sock, 0, SRTO_TSBPDMODE,
160         &(int) { 1 }, sizeof( int ) );
161
162     /* This is an access module so it is always a receiver */
163     srt_setsockopt( p_sys->sock, 0, SRTO_SENDER,
164         &(int) { 0 }, sizeof( int ) );
165
166     /* Set latency */
167     i_latency = var_InheritInteger( p_stream, "latency" );
168     srt_setsockopt( p_sys->sock, 0, SRTO_TSBPDDELAY,
169         &i_latency, sizeof( int ) );
170
171     psz_passphrase = var_InheritString( p_stream, "passphrase" );
172     if ( psz_passphrase != NULL && psz_passphrase[0] != '\0')
173     {
174         int i_key_length = var_InheritInteger( p_stream, "key-length" );
175         srt_setsockopt( p_sys->sock, 0, SRTO_PASSPHRASE,
176             psz_passphrase, strlen( psz_passphrase ) );
177         srt_setsockopt( p_sys->sock, 0, SRTO_PBKEYLEN,
178             &i_key_length, sizeof( int ) );
179     }
180
181     srt_epoll_add_usock( p_sys->i_poll_id, p_sys->sock,
182         &(int) { SRT_EPOLL_ERR | SRT_EPOLL_IN });
183
184     /* Schedule a connect */
185     msg_Dbg( p_stream, "Schedule SRT connect (dest addresss: %s, port: %d).",
186         p_sys->psz_host, p_sys->i_port);
187
188     stat = srt_connect( p_sys->sock, res->ai_addr, res->ai_addrlen);
189     if ( stat == SRT_ERROR )
190     {
191         msg_Err( p_stream, "Failed to connect to server (reason: %s)",
192                  srt_getlasterror_str() );
193         failed = true;
194     }
195
196 out:
197     if (failed && p_sys->sock != SRT_INVALID_SOCK)
198     {
199         srt_epoll_remove_usock( p_sys->i_poll_id, p_sys->sock );
200         srt_close(p_sys->sock);
201         p_sys->sock = SRT_INVALID_SOCK;
202     }
203
204     freeaddrinfo( res );
205     free( psz_passphrase );
206
207     return !failed;
208 }
209
210 static block_t *BlockSRT(stream_t *p_stream, bool *restrict eof)
211 {
212     stream_sys_t *p_sys = p_stream->p_sys;
213     int i_chunk_size = var_InheritInteger( p_stream, "chunk-size" );
214     int i_poll_timeout = var_InheritInteger( p_stream, "poll-timeout" );
215
216     if ( vlc_killed() )
217     {
218         /* We are told to stop. Stop. */
219         return NULL;
220     }
221
222     block_t *pkt = block_Alloc( i_chunk_size );
223     if ( unlikely( pkt == NULL ) )
224     {
225         return NULL;
226     }
227
228     vlc_interrupt_register( srt_wait_interrupted, p_stream);
229
230     SRTSOCKET ready[1];
231     int readycnt = 1;
232     while ( srt_epoll_wait( p_sys->i_poll_id,
233         ready, &readycnt, 0, 0,
234         i_poll_timeout, NULL, 0, NULL, 0 ) >= 0)
235     {
236         if ( readycnt < 0  || ready[0] != p_sys->sock )
237         {
238             /* should never happen, force recovery */
239             srt_close(p_sys->sock);
240             p_sys->sock = SRT_INVALID_SOCK;
241         }
242
243         switch( srt_getsockstate( p_sys->sock ) )
244         {
245             case SRTS_CONNECTED:
246                 /* Good to go */
247                 break;
248             case SRTS_BROKEN:
249             case SRTS_NONEXIST:
250             case SRTS_CLOSED:
251                 /* Failed. Schedule recovery. */
252                 if ( !srt_schedule_reconnect( p_stream ) )
253                     msg_Err( p_stream, "Failed to schedule connect" );
254                 /* Fall-through */
255             default:
256                 /* Not ready */
257                 continue;
258         }
259
260         int stat = srt_recvmsg( p_sys->sock,
261             (char *)pkt->p_buffer, i_chunk_size );
262         if ( stat > 0 )
263         {
264             pkt->i_buffer = stat;
265             goto out;
266         }
267
268         msg_Err( p_stream, "failed to receive packet, set EOS (reason: %s)",
269             srt_getlasterror_str() );
270         *eof = true;
271         break;
272     }
273
274     /* if the poll reports errors for any reason at all
275      * including a timeout, or there is a read error,
276      * we skip the turn.
277      */
278
279     block_Release(pkt);
280     pkt = NULL;
281
282 out:
283     vlc_interrupt_unregister();
284
285     /* Re-add the socket to the poll if we were interrupted */
286     vlc_mutex_lock( &p_sys->lock );
287     if ( p_sys->b_interrupted )
288     {
289         srt_epoll_add_usock( p_sys->i_poll_id, p_sys->sock,
290             &(int) { SRT_EPOLL_ERR | SRT_EPOLL_IN } );
291         p_sys->b_interrupted = false;
292     }
293     vlc_mutex_unlock( &p_sys->lock );
294
295     return pkt;
296 }
297
298 static int Open(vlc_object_t *p_this)
299 {
300     stream_t     *p_stream = (stream_t*)p_this;
301     stream_sys_t *p_sys = NULL;
302     vlc_url_t     parsed_url = { 0 };
303
304     p_sys = vlc_obj_calloc( p_this, 1, sizeof( *p_sys ) );
305     if( unlikely( p_sys == NULL ) )
306         return VLC_ENOMEM;
307
308     srt_startup();
309
310     vlc_mutex_init( &p_sys->lock );
311
312     p_stream->p_sys = p_sys;
313
314     if ( vlc_UrlParse( &parsed_url, p_stream->psz_url ) == -1 )
315     {
316         msg_Err( p_stream, "Failed to parse input URL (%s)",
317             p_stream->psz_url );
318         goto failed;
319     }
320
321     p_sys->psz_host = strdup( parsed_url.psz_host );
322     p_sys->i_port = parsed_url.i_port;
323
324     vlc_UrlClean( &parsed_url );
325
326     p_sys->i_poll_id = srt_epoll_create();
327     if ( p_sys->i_poll_id == -1 )
328     {
329         msg_Err( p_stream, "Failed to create poll id for SRT socket." );
330         goto failed;
331     }
332
333     if ( !srt_schedule_reconnect( p_stream ) )
334     {
335         msg_Err( p_stream, "Failed to schedule connect");
336
337         goto failed;
338     }
339
340     p_stream->pf_block = BlockSRT;
341     p_stream->pf_control = Control;
342
343     return VLC_SUCCESS;
344
345 failed:
346     vlc_mutex_destroy( &p_sys->lock );
347
348     if ( p_sys != NULL )
349     {
350         if ( p_sys->sock != -1 ) srt_close( p_sys->sock );
351         if ( p_sys->i_poll_id != -1 ) srt_epoll_release( p_sys->i_poll_id );
352
353         free( p_sys->psz_host );
354
355         vlc_obj_free( p_this, p_sys );
356         p_stream->p_sys = NULL;
357     }
358
359     return VLC_EGENERIC;
360 }
361
362 static void Close(vlc_object_t *p_this)
363 {
364     stream_t     *p_stream = (stream_t*)p_this;
365     stream_sys_t *p_sys = p_stream->p_sys;
366
367     if ( p_sys )
368     {
369         vlc_mutex_destroy( &p_sys->lock );
370
371         srt_epoll_remove_usock( p_sys->i_poll_id, p_sys->sock );
372         srt_close( p_sys->sock );
373         srt_epoll_release( p_sys->i_poll_id );
374
375         free( p_sys->psz_host );
376
377         vlc_obj_free( p_this, p_sys );
378         p_stream->p_sys = NULL;
379     }
380
381     srt_cleanup();
382 }
383
384 /* Module descriptor */
385 vlc_module_begin ()
386     set_shortname( N_("SRT") )
387     set_description( N_("SRT input") )
388     set_category( CAT_INPUT )
389     set_subcategory( SUBCAT_INPUT_ACCESS )
390
391     add_integer( "chunk-size", SRT_DEFAULT_CHUNK_SIZE,
392             N_("SRT chunk size (bytes)"), NULL, true )
393     add_integer( "poll-timeout", SRT_DEFAULT_POLL_TIMEOUT,
394             N_("Return poll wait after timeout milliseconds (-1 = infinite)"), NULL, true )
395     add_integer( "latency", SRT_DEFAULT_LATENCY, N_("SRT latency (ms)"), NULL, true )
396     add_password("passphrase", "", N_("Password for stream encryption"), NULL)
397     add_integer( "key-length", SRT_DEFAULT_KEY_LENGTH,
398             SRT_KEY_LENGTH_TEXT, SRT_KEY_LENGTH_TEXT, false )
399         change_integer_list( srt_key_lengths, srt_key_length_names )
400
401     set_capability( "access", 0 )
402     add_shortcut( "srt" )
403
404     set_callbacks( Open, Close )
405 vlc_module_end ()