1 /*****************************************************************************
2 * srt.c: SRT (Secure Reliable Transport) output module
3 *****************************************************************************
4 * Copyright (C) 2017, Collabora Ltd.
6 * Authors: Justin Kim <justin.kim@collabora.com>
8 * This program is free software; you can redistribute it and/or modify it
9 * under the terms of the GNU Lesser General Public License as published by
10 * the Free Software Foundation; either version 2.1 of the License, or
11 * (at your option) any later version.
13 * This program is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 * GNU Lesser General Public License for more details.
18 * You should have received a copy of the GNU Lesser General Public License
19 * along with this program; if not, write to the Free Software Foundation,
20 * Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
21 *****************************************************************************/
30 #include <vlc_common.h>
31 #include <vlc_interrupt.h>
33 #include <vlc_plugin.h>
35 #include <vlc_block.h>
36 #include <vlc_network.h>
40 /* libsrt defines default packet size as 1316 internally
41 * so srt module takes same value. */
42 #define SRT_DEFAULT_CHUNK_SIZE 1316
43 /* libsrt tutorial uses 9000 as a default binding port */
44 #define SRT_DEFAULT_PORT 9000
45 /* The default timeout is -1 (infinite) */
46 #define SRT_DEFAULT_POLL_TIMEOUT 100
47 /* The default latency is 125
48 * which uses srt library internally */
49 #define SRT_DEFAULT_LATENCY 125
50 /* Crypto key length in bytes. */
51 #define SRT_KEY_LENGTH_TEXT N_("Crypto key length in bytes")
52 #define SRT_DEFAULT_KEY_LENGTH 16
53 static const int srt_key_lengths
[] = {
57 static const char *const srt_key_length_names
[] = {
58 N_("16 bytes"), N_("24 bytes"), N_("32 bytes"),
61 struct sout_access_out_sys_t
71 static void srt_wait_interrupted(void *p_data
)
73 sout_access_out_t
*p_access
= p_data
;
74 sout_access_out_sys_t
*p_sys
= p_access
->p_sys
;
75 msg_Dbg( p_access
, "Waking up srt_epoll_wait");
76 if ( write( p_sys
->i_pipe_fds
[1], &( bool ) { true }, sizeof( bool ) ) < 0 )
78 msg_Err( p_access
, "Failed to send data to event fd");
82 static ssize_t
Write( sout_access_out_t
*p_access
, block_t
*p_buffer
)
84 sout_access_out_sys_t
*p_sys
= p_access
->p_sys
;
87 vlc_interrupt_register( srt_wait_interrupted
, p_access
);
93 i_len
+= p_buffer
->i_buffer
;
95 while( p_buffer
->i_buffer
)
97 size_t i_write
= __MIN( p_buffer
->i_buffer
, p_sys
->i_chunk_size
);
101 if ( srt_epoll_wait( p_sys
->i_poll_id
,
102 0, 0, ready
, &(int){ 2 }, p_sys
->i_poll_timeout
,
103 &(int) { p_sys
->i_pipe_fds
[0] }, &(int) { 1 }, NULL
, 0 ) == -1 )
105 /* Assuming that timeout error is normal when SRT socket is connected. */
106 if ( srt_getlasterror( NULL
) == SRT_ETIMEOUT
&&
107 srt_getsockstate( p_sys
->sock
) == SRTS_CONNECTED
)
109 srt_clearlasterror();
113 i_len
= VLC_EGENERIC
;
118 int ret
= read( p_sys
->i_pipe_fds
[0], &cancel
, sizeof( bool ) );
119 if ( ret
> 0 && cancel
)
121 msg_Dbg( p_access
, "Cancelled running" );
126 if ( srt_sendmsg2( p_sys
->sock
, (char *)p_buffer
->p_buffer
, i_write
, 0 ) == SRT_ERROR
)
127 msg_Warn( p_access
, "send error: %s", srt_getlasterror_str() );
129 p_buffer
->p_buffer
+= i_write
;
130 p_buffer
->i_buffer
-= i_write
;
133 p_next
= p_buffer
->p_next
;
134 block_Release( p_buffer
);
139 vlc_interrupt_unregister();
140 if ( i_len
<= 0 ) block_ChainRelease( p_buffer
);
144 static int Control( sout_access_out_t
*p_access
, int i_query
, va_list args
)
146 VLC_UNUSED (p_access
);
148 int i_ret
= VLC_SUCCESS
;
152 case ACCESS_OUT_CONTROLS_PACE
:
153 *va_arg( args
, bool * ) = false;
157 i_ret
= VLC_EGENERIC
;
164 static int Open( vlc_object_t
*p_this
)
166 sout_access_out_t
*p_access
= (sout_access_out_t
*)p_this
;
167 sout_access_out_sys_t
*p_sys
= NULL
;
169 char *psz_dst_addr
= NULL
;
172 char *psz_passphrase
= NULL
;
174 struct addrinfo hints
= {
175 .ai_socktype
= SOCK_DGRAM
,
178 if (var_Create ( p_access
, "dst-port", VLC_VAR_INTEGER
)
179 || var_Create ( p_access
, "src-port", VLC_VAR_INTEGER
)
180 || var_Create ( p_access
, "dst-addr", VLC_VAR_STRING
)
181 || var_Create ( p_access
, "src-addr", VLC_VAR_STRING
) )
183 msg_Err( p_access
, "Valid network information is required." );
187 if( !( p_sys
= malloc ( sizeof( *p_sys
) ) ) )
190 p_sys
->i_chunk_size
= var_InheritInteger( p_access
, "chunk-size" );
191 p_sys
->i_poll_timeout
= var_InheritInteger( p_access
, "poll-timeout" );
192 p_sys
->i_latency
= var_InheritInteger( p_access
, "latency" );
193 p_sys
->i_poll_id
= -1;
195 p_access
->p_sys
= p_sys
;
197 psz_passphrase
= var_InheritString( p_access
, "passphrase" );
199 i_dst_port
= SRT_DEFAULT_PORT
;
200 char *psz_parser
= psz_dst_addr
= strdup( p_access
->psz_path
);
207 if (psz_parser
[0] == '[')
208 psz_parser
= strchr (psz_parser
, ']');
210 psz_parser
= strchr (psz_parser
? psz_parser
: psz_dst_addr
, ':');
211 if (psz_parser
!= NULL
)
213 *psz_parser
++ = '\0';
214 i_dst_port
= atoi (psz_parser
);
217 msg_Dbg( p_access
, "Setting SRT socket (dest addresss: %s, port: %d).",
218 psz_dst_addr
, i_dst_port
);
220 stat
= vlc_getaddrinfo( psz_dst_addr
, i_dst_port
, &hints
, &res
);
223 msg_Err( p_access
, "Cannot resolve [%s]:%d (reason: %s)",
226 gai_strerror( stat
) );
231 p_sys
->sock
= srt_socket( res
->ai_family
, SOCK_DGRAM
, 0 );
232 if ( p_sys
->sock
== SRT_ERROR
)
234 msg_Err( p_access
, "Failed to open socket." );
238 /* Make SRT non-blocking */
239 srt_setsockopt( p_sys
->sock
, 0, SRTO_SNDSYN
, &(bool) { false }, sizeof( bool ) );
241 /* Make sure TSBPD mode is enable (SRT mode) */
242 srt_setsockopt( p_sys
->sock
, 0, SRTO_TSBPDMODE
, &(int) { 1 }, sizeof( int ) );
244 /* This is an access_out so it is always a sender */
245 srt_setsockopt( p_sys
->sock
, 0, SRTO_SENDER
, &(int) { 1 }, sizeof( int ) );
248 srt_setsockopt( p_sys
->sock
, 0, SRTO_TSBPDDELAY
, &p_sys
->i_latency
, sizeof( int ) );
250 if ( psz_passphrase
!= NULL
&& psz_passphrase
[0] != '\0')
252 int i_key_length
= var_InheritInteger( p_access
, "key-length" );
253 srt_setsockopt( p_sys
->sock
, 0, SRTO_PASSPHRASE
,
254 psz_passphrase
, strlen( psz_passphrase
) );
255 srt_setsockopt( p_sys
->sock
, 0, SRTO_PBKEYLEN
,
256 &i_key_length
, sizeof( int ) );
259 p_sys
->i_poll_id
= srt_epoll_create();
260 if ( p_sys
->i_poll_id
== -1 )
262 msg_Err( p_access
, "Failed to create poll id for SRT socket (reason: %s)",
263 srt_getlasterror_str() );
268 if ( vlc_pipe( p_sys
->i_pipe_fds
) != 0 )
270 msg_Err( p_access
, "Failed to create pipe fds." );
273 fcntl( p_sys
->i_pipe_fds
[0], F_SETFL
, O_NONBLOCK
| fcntl( p_sys
->i_pipe_fds
[0], F_GETFL
) );
275 srt_epoll_add_usock( p_sys
->i_poll_id
, p_sys
->sock
, &(int) { SRT_EPOLL_OUT
});
276 srt_setsockopt( p_sys
->sock
, 0, SRTO_SENDER
, &(int) { 1 }, sizeof(int) );
278 srt_epoll_add_ssock( p_sys
->i_poll_id
, p_sys
->i_pipe_fds
[0], &(int) { SRT_EPOLL_IN
} );
280 stat
= srt_connect( p_sys
->sock
, res
->ai_addr
, sizeof (struct sockaddr
));
281 if ( stat
== SRT_ERROR
)
283 msg_Err( p_access
, "Failed to connect to server (reason: %s)",
284 srt_getlasterror_str() );
288 p_access
->pf_write
= Write
;
289 p_access
->pf_control
= Control
;
291 free( psz_passphrase
);
292 free( psz_dst_addr
);
298 free( psz_passphrase
);
300 if ( psz_dst_addr
!= NULL
)
301 free( psz_dst_addr
);
306 vlc_close( p_sys
->i_pipe_fds
[0] );
307 vlc_close( p_sys
->i_pipe_fds
[1] );
311 if ( p_sys
->i_poll_id
!= -1 ) srt_epoll_release( p_sys
->i_poll_id
);
312 if ( p_sys
->sock
!= -1 ) srt_close( p_sys
->sock
);
320 static void Close( vlc_object_t
* p_this
)
322 sout_access_out_t
*p_access
= (sout_access_out_t
*)p_this
;
323 sout_access_out_sys_t
*p_sys
= p_access
->p_sys
;
325 srt_epoll_release( p_sys
->i_poll_id
);
326 srt_close( p_sys
->sock
);
328 vlc_close( p_sys
->i_pipe_fds
[0] );
329 vlc_close( p_sys
->i_pipe_fds
[1] );
334 /* Module descriptor */
336 set_shortname( N_("SRT") )
337 set_description( N_("SRT stream output") )
338 set_category( CAT_SOUT
)
339 set_subcategory( SUBCAT_SOUT_ACO
)
341 add_integer( "chunk-size", SRT_DEFAULT_CHUNK_SIZE
,
342 N_("SRT chunk size (bytes)"), NULL
, true )
343 add_integer( "poll-timeout", SRT_DEFAULT_POLL_TIMEOUT
,
344 N_("Return poll wait after timeout milliseconds (-1 = infinite)"), NULL
, true )
345 add_integer( "latency", SRT_DEFAULT_LATENCY
, N_("SRT latency (ms)"), NULL
, true )
346 add_password( "passphrase", "", N_("Password for stream encryption"), NULL
, false )
347 add_integer( "key-length", SRT_DEFAULT_KEY_LENGTH
,
348 SRT_KEY_LENGTH_TEXT
, SRT_KEY_LENGTH_TEXT
, false )
349 change_integer_list( srt_key_lengths
, srt_key_length_names
)
351 set_capability( "sout access", 0 )
352 add_shortcut( "srt" )
354 set_callbacks( Open
, Close
)