You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

321 lines
12 KiB

/*****************************************************************************
* rist.c: RIST (Reliable Internet Stream Transport) output module
*****************************************************************************
* Copyright (C) 2021, SipRadius LLC
*
* Authors: Sergio Ammirata <sergio@ammirata.net>
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation; either version 2.1 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
*****************************************************************************/
#ifdef HAVE_CONFIG_H
# include "config.h"
#endif
#include <vlc_common.h>
#include <vlc_configuration.h>
#include <vlc_interrupt.h>
#include <vlc_plugin.h>
#include <vlc_sout.h>
#include <vlc_block.h>
#include <vlc_rand.h>
#include <sys/time.h>
#define RIST_CFG_PREFIX "sout-rist-"
#include "../access/rist.h"
static const char *const ppsz_sout_options[] = {
RIST_CFG_MAX_PACKET_SIZE,
RIST_URL_PARAM_VIRT_SRC_PORT,
RIST_URL_PARAM_VIRT_DST_PORT,
RIST_CFG_LATENCY,
"multipeer-mode",
RIST_CFG_URL2,
RIST_CFG_URL3,
RIST_CFG_URL4,
RIST_URL_PARAM_BANDWIDTH,
RIST_CFG_RETRY_INTERVAL,
RIST_URL_PARAM_REORDER_BUFFER,
RIST_CFG_MAX_RETRIES,
RIST_URL_PARAM_VERBOSE_LEVEL,
RIST_URL_PARAM_CNAME,
RIST_URL_PARAM_PROFILE,
RIST_URL_PARAM_SECRET,
RIST_URL_PARAM_AES_TYPE,
RIST_URL_PARAM_SRP_USERNAME,
RIST_URL_PARAM_SRP_PASSWORD,
NULL
};
typedef struct
{
struct rist_ctx *sender_ctx;
int gre_src_port;
int gre_dst_port;
uint32_t i_recovery_buffer;
size_t i_max_packet_size;
struct rist_logging_settings logging_settings;
} sout_access_out_sys_t;
static int cb_stats(void *arg, const struct rist_stats *stats_container)
{
sout_access_out_t *p_access = (sout_access_out_t*)arg;
sout_access_out_sys_t *p_sys = p_access->p_sys;
msg_Dbg(p_access, "[RIST-STATS]: %s", stats_container->stats_json);
const struct rist_stats_sender_peer *stats_sender_peer = &stats_container->stats.sender_peer;
msg_Dbg(p_access, "[RIST-STATS]: name %s, id %"PRIu32", bitrate %zu, sent %"PRIu64", received %"PRIu64", retransmitted %"PRIu64", Q %.2f, rtt %"PRIu32"ms",
stats_sender_peer->cname,
stats_sender_peer->peer_id,
stats_sender_peer->bandwidth,
stats_sender_peer->sent,
stats_sender_peer->received,
stats_sender_peer->retransmitted,
stats_sender_peer->quality,
stats_sender_peer->rtt
);
if (stats_sender_peer->rtt > p_sys->i_recovery_buffer)
{
msg_Err(p_access, "The RTT between us and the receiver is higher than the configured recovery buffer size, %"PRIu32" > %"PRIu32" ms, you should increase the recovery buffer size",
stats_sender_peer->rtt, p_sys->i_recovery_buffer);
}
rist_stats_free(stats_container);
return 0;
}
static ssize_t Write( sout_access_out_t *p_access, block_t *p_buffer )
{
sout_access_out_sys_t *p_sys = p_access->p_sys;
int i_len = 0;
struct rist_data_block rist_buffer = { 0 };
rist_buffer.virt_src_port = p_sys->gre_src_port;
rist_buffer.virt_dst_port = p_sys->gre_dst_port;
while( p_buffer )
{
block_t *p_next;
i_len += p_buffer->i_buffer;
while( p_buffer->i_buffer )
{
size_t i_write = __MIN( p_buffer->i_buffer, p_sys->i_max_packet_size );
rist_buffer.payload = p_buffer->p_buffer;
rist_buffer.payload_len = p_buffer->i_buffer;
rist_sender_data_write(p_sys->sender_ctx, &rist_buffer);
p_buffer->p_buffer += i_write;
p_buffer->i_buffer -= i_write;
}
p_next = p_buffer->p_next;
block_Release( p_buffer );
p_buffer = p_next;
}
return i_len;
}
static int Control( sout_access_out_t *p_access, int i_query, va_list args )
{
VLC_UNUSED( p_access );
int i_ret = VLC_SUCCESS;
switch( i_query )
{
case ACCESS_OUT_CONTROLS_PACE:
*va_arg( args, bool * ) = false;
break;
default:
i_ret = VLC_EGENERIC;
break;
}
return i_ret;
}
static void Close( vlc_object_t * p_this )
{
sout_access_out_t *p_access = (sout_access_out_t*)p_this;
sout_access_out_sys_t *p_sys = p_access->p_sys;
rist_destroy(p_sys->sender_ctx);
p_sys->sender_ctx = NULL;
}
static int Open( vlc_object_t *p_this )
{
sout_access_out_t *p_access = (sout_access_out_t*)p_this;
sout_access_out_sys_t *p_sys = NULL;
if (var_Create ( p_access, "dst-port", VLC_VAR_INTEGER )
|| var_Create ( p_access, "src-port", VLC_VAR_INTEGER )
|| var_Create ( p_access, "dst-addr", VLC_VAR_STRING )
|| var_Create ( p_access, "src-addr", VLC_VAR_STRING ) )
{
msg_Err( p_access, "Valid network information is required." );
return VLC_EGENERIC;
}
config_ChainParse( p_access, RIST_CFG_PREFIX, ppsz_sout_options, p_access->p_cfg );
p_sys = vlc_obj_calloc( p_this, 1, sizeof( *p_sys ) );
if( unlikely( p_sys == NULL ) )
return VLC_ENOMEM;
p_access->p_sys = p_sys;
p_sys->gre_src_port = var_InheritInteger(p_access, RIST_CFG_PREFIX RIST_URL_PARAM_VIRT_SRC_PORT);
p_sys->gre_dst_port = var_InheritInteger(p_access, RIST_CFG_PREFIX RIST_URL_PARAM_VIRT_DST_PORT);
if (p_sys->gre_dst_port % 2 != 0) {
msg_Err( p_access, "Virtual destination port must be an even number." );
return VLC_EGENERIC;
}
p_sys->i_max_packet_size = rist_get_max_packet_size(VLC_OBJECT(p_access));
int i_rist_profile = var_InheritInteger(p_access, RIST_CFG_PREFIX RIST_URL_PARAM_PROFILE);
int i_verbose_level = var_InheritInteger(p_access, RIST_CFG_PREFIX RIST_URL_PARAM_VERBOSE_LEVEL);
//disable global logging: see comment in access/rist.c
struct rist_logging_settings rist_global_logging_settings = LOGGING_SETTINGS_INITIALIZER;
if (rist_logging_set_global(&rist_global_logging_settings) != 0) {
msg_Err(p_access,"Could not set logging\n");
return VLC_EGENERIC;
}
struct rist_logging_settings *logging_settings = &p_sys->logging_settings;
logging_settings->log_socket = -1;
logging_settings->log_stream = NULL;
logging_settings->log_level = i_verbose_level;
logging_settings->log_cb = log_cb;
logging_settings->log_cb_arg = p_access;
if (rist_sender_create(&p_sys->sender_ctx, i_rist_profile, 0, logging_settings) != 0) {
msg_Err( p_access, "Could not create rist sender context\n");
return VLC_EGENERIC;
}
// Enable stats data
if (rist_stats_callback_set(p_sys->sender_ctx, 1000, cb_stats, (void *)p_access) == -1) {
msg_Err(p_access, "Could not enable stats callback");
goto failed;
}
int i_multipeer_mode = var_InheritInteger(p_access, RIST_CFG_PREFIX "multipeer-mode");
int i_recovery_length = var_InheritInteger(p_access, RIST_CFG_PREFIX RIST_CFG_LATENCY);
p_sys->i_recovery_buffer = i_recovery_length;
if ( !rist_add_peers(VLC_OBJECT(p_access), p_sys->sender_ctx, p_access->psz_path, i_multipeer_mode, p_sys->gre_dst_port + 1, i_recovery_length) )
goto failed;
if (rist_start(p_sys->sender_ctx) == -1) {
msg_Err( p_access, "Could not start rist sender\n");
goto failed;
}
p_access->pf_write = Write;
p_access->pf_control = Control;
return VLC_SUCCESS;
failed:
rist_destroy(p_sys->sender_ctx);
p_sys->sender_ctx = NULL;
return VLC_EGENERIC;
}
#define SRC_PORT_TEXT N_("Virtual Source Port")
#define SRC_PORT_LONGTEXT N_( \
"Source port to be used inside the reduced-mode of the main profile" )
#define DST_PORT_TEXT N_("Virtual Destination Port")
#define DST_PORT_LONGTEXT N_( \
"Destination port to be used inside the reduced-mode of the main profile" )
/* The default target payload size */
#define RIST_DEFAULT_TARGET_PAYLOAD_SIZE 1316
/* Multipeer mode */
#define RIST_DEFAULT_MULTIPEER_MODE 0
#define RIST_MULTIPEER_MODE_TEXT N_("Multipeer mode")
#define RIST_MULTIPEER_MODE_LONGTEXT N_( \
"This allows you to select between duplicate or load balanced modes when " \
"sending data to multiple peers (several network paths)" )
static const int multipeer_mode_type[] = { 0, 5, };
static const char *const multipeer_mode_type_names[] = {
N_("Duplicate"), N_("Load balanced"),
};
/* Module descriptor */
vlc_module_begin()
set_shortname( N_("RIST") )
set_description( N_("RIST stream output") )
set_subcategory( SUBCAT_SOUT_ACO )
add_integer( RIST_CFG_PREFIX RIST_CFG_MAX_PACKET_SIZE, RIST_DEFAULT_TARGET_PAYLOAD_SIZE,
N_("RIST target payload size (bytes)"), NULL )
add_integer( RIST_CFG_PREFIX RIST_URL_PARAM_VIRT_SRC_PORT, RIST_DEFAULT_VIRT_SRC_PORT,
SRC_PORT_TEXT, SRC_PORT_LONGTEXT )
add_integer( RIST_CFG_PREFIX RIST_URL_PARAM_VIRT_DST_PORT, RIST_DEFAULT_VIRT_DST_PORT,
DST_PORT_TEXT, DST_PORT_LONGTEXT )
add_integer( RIST_CFG_PREFIX "multipeer-mode", RIST_DEFAULT_MULTIPEER_MODE,
RIST_MULTIPEER_MODE_TEXT, RIST_MULTIPEER_MODE_LONGTEXT )
change_integer_list( multipeer_mode_type, multipeer_mode_type_names )
add_string( RIST_CFG_PREFIX RIST_CFG_URL2, NULL, RIST_URL2_TEXT, NULL )
add_string( RIST_CFG_PREFIX RIST_CFG_URL3, NULL, RIST_URL3_TEXT, NULL )
add_string( RIST_CFG_PREFIX RIST_CFG_URL4, NULL, RIST_URL4_TEXT, NULL )
add_integer( RIST_CFG_PREFIX RIST_URL_PARAM_BANDWIDTH, RIST_DEFAULT_RECOVERY_MAXBITRATE,
RIST_MAX_BITRATE_TEXT, RIST_MAX_BITRATE_LONGTEXT )
add_integer( RIST_CFG_PREFIX RIST_CFG_RETRY_INTERVAL, RIST_DEFAULT_RECOVERY_RTT_MIN,
RIST_RETRY_INTERVAL_TEXT, NULL )
add_integer( RIST_CFG_PREFIX RIST_URL_PARAM_REORDER_BUFFER, RIST_DEFAULT_RECOVERY_REORDER_BUFFER,
RIST_REORDER_BUFFER_TEXT, NULL )
add_integer( RIST_CFG_PREFIX RIST_CFG_MAX_RETRIES, RIST_DEFAULT_MAX_RETRIES,
RIST_MAX_RETRIES_TEXT, NULL )
add_integer( RIST_CFG_PREFIX RIST_URL_PARAM_VERBOSE_LEVEL, RIST_DEFAULT_VERBOSE_LEVEL,
RIST_VERBOSE_LEVEL_TEXT, RIST_VERBOSE_LEVEL_LONGTEXT )
change_integer_list( verbose_level_type, verbose_level_type_names )
add_integer( RIST_CFG_PREFIX RIST_CFG_LATENCY, RIST_DEFAULT_RECOVERY_LENGHT_MIN,
BUFFER_TEXT, BUFFER_LONGTEXT )
add_string( RIST_CFG_PREFIX RIST_URL_PARAM_CNAME, NULL, RIST_CNAME_TEXT,
RIST_CNAME_LONGTEXT )
add_integer( RIST_CFG_PREFIX RIST_URL_PARAM_PROFILE, RIST_DEFAULT_PROFILE,
RIST_PROFILE_TEXT, RIST_PROFILE_LONGTEXT )
add_password( RIST_CFG_PREFIX RIST_URL_PARAM_SECRET, "",
RIST_SHARED_SECRET_TEXT, NULL )
add_integer( RIST_CFG_PREFIX RIST_URL_PARAM_AES_TYPE, 0,
RIST_ENCRYPTION_TYPE_TEXT, NULL )
change_integer_list( rist_encryption_type, rist_encryption_type_names )
add_string( RIST_CFG_PREFIX RIST_URL_PARAM_SRP_USERNAME, "",
RIST_SRP_USERNAME_TEXT, NULL )
add_password( RIST_CFG_PREFIX RIST_URL_PARAM_SRP_PASSWORD, "",
RIST_SRP_PASSWORD_TEXT, NULL )
set_capability( "sout access", 10 )
add_shortcut( "librist", "rist", "tr06" )
set_callbacks( Open, Close )
vlc_module_end ()