<commit_before>/*****************************************************************************
 * rtp.c: rtp stream output module
 *****************************************************************************
 * Copyright (C) 2003-2004 the VideoLAN team
 * Copyright © 2007-2008 <NAME>
 *
 * Authors: <NAME> <<EMAIL>>
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 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 General Public License for more details.
 *
 * You should have received a copy of the GNU 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.
 *****************************************************************************/

/*****************************************************************************
 * Preamble
 *****************************************************************************/

#ifdef HAVE_CONFIG_H
# include "config.h"
#endif

#include <vlc_common.h>
#include <vlc_plugin.h>
#include <vlc_sout.h>
#include <vlc_block.h>

#include <vlc_httpd.h>
#include <vlc_url.h>
#include <vlc_network.h>
#include <vlc_charset.h>
#include <vlc_strings.h>
#include <vlc_rand.h>
#include <srtp.h>

#include "rtp.h"

#ifdef HAVE_UNISTD_H
#   include <sys/types.h>
#   include <unistd.h>
#   include <fcntl.h>
#   include <sys/stat.h>
#endif
#ifdef HAVE_LINUX_DCCP_H
#   include <linux/dccp.h>
#endif
#ifndef IPPROTO_DCCP
# define IPPROTO_DCCP 33
#endif
#ifndef IPPROTO_UDPLITE
# define IPPROTO_UDPLITE 136
#endif

#include <errno.h>

#include <assert.h>

/*****************************************************************************
 * Module descriptor
 *****************************************************************************/

#define DEST_TEXT N_("Destination")
#define DEST_LONGTEXT N_( \
    "This is the output URL that will be used." )
#define SDP_TEXT N_("SDP")
#define SDP_LONGTEXT N_( \
    "This allows you to specify how the SDP (Session Descriptor) for this RTP "\
    "session will be made available. You must use an url: http://location to " \
    "access the SDP via HTTP, rtsp://location for RTSP access, and sap:// " \
    "for the SDP to be announced via SAP." )
#define SAP_TEXT N_("SAP announcing")
#define SAP_LONGTEXT N_("Announce this session with SAP.")
#define MUX_TEXT N_("Muxer")
#define MUX_LONGTEXT N_( \
    "This allows you to specify the muxer used for the streaming output. " \
    "Default is to use no muxer (standard RTP stream)." )

#define NAME_TEXT N_("Session name")
#define NAME_LONGTEXT N_( \
    "This is the name of the session that will be announced in the SDP " \
    "(Session Descriptor)." )
#define DESC_TEXT N_("Session description")
#define DESC_LONGTEXT N_( \
    "This allows you to give a short description with details about the stream, " \
    "that will be announced in the SDP (Session Descriptor)." )
#define URL_TEXT N_("Session URL")
#define URL_LONGTEXT N_( \
    "This allows you to give an URL with more details about the stream " \
    "(often the website of the streaming organization), that will " \
    "be announced in the SDP (Session Descriptor)." )
#define EMAIL_TEXT N_("Session email")
#define EMAIL_LONGTEXT N_( \
    "This allows you to give a contact mail address for the stream, that will " \
    "be announced in the SDP (Session Descriptor)." )
#define PHONE_TEXT N_("Session phone number")
#define PHONE_LONGTEXT N_( \
    "This allows you to give a contact telephone number for the stream, that will " \
    "be announced in the SDP (Session Descriptor)." )

#define PORT_TEXT N_("Port")
#define PORT_LONGTEXT N_( \
    "This allows you to specify the base port for the RTP streaming." )
#define PORT_AUDIO_TEXT N_("Audio port")
#define PORT_AUDIO_LONGTEXT N_( \
    "This allows you to specify the default audio port for the RTP streaming." )
#define PORT_VIDEO_TEXT N_("Video port")
#define PORT_VIDEO_LONGTEXT N_( \
    "This allows you to specify the default video port for the RTP streaming." )

#define TTL_TEXT N_("Hop limit (TTL)")
#define TTL_LONGTEXT N_( \
    "This is the hop limit (also known as \"Time-To-Live\" or TTL) of " \
    "the multicast packets sent by the stream output (-1 = use operating " \
    "system built-in default).")

#define RTCP_MUX_TEXT N_("RTP/RTCP multiplexing")
#define RTCP_MUX_LONGTEXT N_( \
    "This sends and receives RTCP packet multiplexed over the same port " \
    "as RTP packets." )

#define PROTO_TEXT N_("Transport protocol")
#define PROTO_LONGTEXT N_( \
    "This selects which transport protocol to use for RTP." )

#define SRTP_KEY_TEXT N_("SRTP key (hexadecimal)")
#define SRTP_KEY_LONGTEXT N_( \
    "RTP packets will be integrity-protected and ciphered "\
    "with this Secure RTP master shared secret key.")

#define SRTP_SALT_TEXT N_("SRTP salt (hexadecimal)")
#define SRTP_SALT_LONGTEXT N_( \
    "Secure RTP requires a (non-secret) master salt value.")

static const char *const ppsz_protos[] = {
    "dccp", "sctp", "tcp", "udp", "udplite",
};

static const char *const ppsz_protocols[] = {
    "DCCP", "SCTP", "TCP", "UDP", "UDP-Lite",
};

#define RFC3016_TEXT N_("MP4A LATM")
#define RFC3016_LONGTEXT N_( \
    "This allows you to stream MPEG4 LATM audio streams (see RFC3016)." )

static int  Open ( vlc_object_t * );
static void Close( vlc_object_t * );

#define SOUT_CFG_PREFIX "sout-rtp-"
#define MAX_EMPTY_BLOCKS 200

vlc_module_begin ()
    set_shortname( N_("RTP"))
    set_description( N_("RTP stream output") )
    set_capability( "sout stream", 0 )
    add_shortcut( "rtp" )
    set_category( CAT_SOUT )
    set_subcategory( SUBCAT_SOUT_STREAM )

    add_string( SOUT_CFG_PREFIX "dst", "", NULL, DEST_TEXT,
                DEST_LONGTEXT, true );
    add_string( SOUT_CFG_PREFIX "sdp", "", NULL, SDP_TEXT,
                SDP_LONGTEXT, true );
    add_string( SOUT_CFG_PREFIX "mux", "", NULL, MUX_TEXT,
                MUX_LONGTEXT, true );
    add_bool( SOUT_CFG_PREFIX "sap", false, NULL, SAP_TEXT, SAP_LONGTEXT,
              true );

    add_string( SOUT_CFG_PREFIX "name", "", NULL, NAME_TEXT,
                NAME_LONGTEXT, true );
    add_string( SOUT_CFG_PREFIX "description", "", NULL, DESC_TEXT,
                DESC_LONGTEXT, true );
    add_string( SOUT_CFG_PREFIX "url", "", NULL, URL_TEXT,
                URL_LONGTEXT, true );
    add_string( SOUT_CFG_PREFIX "email", "", NULL, EMAIL_TEXT,
                EMAIL_LONGTEXT, true );
    add_string( SOUT_CFG_PREFIX "phone", "", NULL, PHONE_TEXT,
                PHONE_LONGTEXT, true );

    add_string( SOUT_CFG_PREFIX "proto", "udp", NULL, PROTO_TEXT,
                PROTO_LONGTEXT, false );
        change_string_list( ppsz_protos, ppsz_protocols, NULL );
    add_integer( SOUT_CFG_PREFIX "port", 5004, NULL, PORT_TEXT,
                 PORT_LONGTEXT, true );
    add_integer( SOUT_CFG_PREFIX "port-audio", 0, NULL, PORT_AUDIO_TEXT,
                 PORT_AUDIO_LONGTEXT, true );
    add_integer( SOUT_CFG_PREFIX "port-video", 0, NULL, PORT_VIDEO_TEXT,
                 PORT_VIDEO_LONGTEXT, true );

    add_integer( SOUT_CFG_PREFIX "ttl", -1, NULL, TTL_TEXT,
                 TTL_LONGTEXT, true );
    add_bool( SOUT_CFG_PREFIX "rtcp-mux", false, NULL,
              RTCP_MUX_TEXT, RTCP_MUX_LONGTEXT, false );

    add_string( SOUT_CFG_PREFIX "key", "", NULL,
                SRTP_KEY_TEXT, SRTP_KEY_LONGTEXT, false );
    add_string( SOUT_CFG_PREFIX "salt", "", NULL,
                SRTP_SALT_TEXT, SRTP_SALT_LONGTEXT, false );

    add_bool( SOUT_CFG_PREFIX "mp4a-latm", 0, NULL, RFC3016_TEXT,
                 RFC3016_LONGTEXT, false );

    set_callbacks( Open, Close )
vlc_module_end ()

/*****************************************************************************
 * Exported prototypes
 *****************************************************************************/
static const char *const ppsz_sout_options[] = {
    "dst", "name", "port", "port-audio", "port-video", "*sdp", "ttl", "mux",
    "sap", "description", "url", "email", "phone",
    "proto", "rtcp-mux", "key", "salt",
    "mp4a-latm", NULL
};

static sout_stream_id_t *Add ( sout_stream_t *, es_format_t * );
static int               Del ( sout_stream_t *, sout_stream_id_t * );
static int               Send( sout_stream_t *, sout_stream_id_t *,
                               block_t* );
static sout_stream_id_t *MuxAdd ( sout_stream_t *, es_format_t * );
static int               MuxDel ( sout_stream_t *, sout_stream_id_t * );
static int               MuxSend( sout_stream_t *, sout_stream_id_t *,
                                  block_t* );

static sout_access_out_t *GrabberCreate( sout_stream_t *p_sout );
static void* ThreadSend( vlc_object_t *p_this );

static void SDPHandleUrl( sout_stream_t *, const char * );

static int SapSetup( sout_stream_t *p_stream );
static int FileSetup( sout_stream_t *p_stream );
static int HttpSetup( sout_stream_t *p_stream, const vlc_url_t * );

struct sout_stream_sys_t
{
    /* SDP */
    char    *psz_sdp;
    vlc_mutex_t  lock_sdp;

    /* SDP to disk */
    bool b_export_sdp_file;
    char *psz_sdp_file;

    /* SDP via SAP */
    bool b_export_sap;
    session_descriptor_t *p_session;

    /* SDP via HTTP */
    httpd_host_t *p_httpd_host;
    httpd_file_t *p_httpd_file;

    /* RTSP */
    rtsp_stream_t *rtsp;

    /* */
    char     *psz_destination;
    uint32_t  payload_bitmap;
    uint16_t  i_port;
    uint16_t  i_port_audio;
    uint16_t  i_port_video;
    uint8_t   proto;
    bool      rtcp_mux;
    int       i_ttl:9;
    bool      b_latm;

    /* in case we do TS/PS over rtp */
    sout_mux_t        *p_mux;
    sout_access_out_t *p_grab;
    block_t           *packet;

    /* */
    vlc_mutex_t      lock_es;
    int              i_es;
    sout_stream_id_t **es;
};

typedef int (*pf_rtp_packetizer_t)( sout_stream_id_t *, block_t * );

typedef struct rtp_sink_t
{
    int rtp_fd;
    rtcp_sender_t *rtcp;
} rtp_sink_t;

struct sout_stream_id_t
{
    VLC_COMMON_MEMBERS

    sout_stream_t *p_stream;
    /* rtp field */
    uint16_t    i_sequence;
    uint8_t     i_payload_type;
    uint8_t     ssrc[4];

    /* for sdp */
    const char  *psz_enc;
    char        *psz_fmtp;
    int          i_clock_rate;
    int          i_port;
    int          i_cat;
    int          i_channels;
    int          i_bitrate;

    /* Packetizer specific fields */
    int                 i_mtu;
    srtp_session_t     *srtp;
    pf_rtp_packetizer_t pf_packetize;

    /* Packets sinks */
    vlc_mutex_t       lock_sink;
    int               sinkc;
    rtp_sink_t       *sinkv;
    rtsp_stream_id_t *rtsp_id;
    int              *listen_fd;

    block_fifo_t     *p_fifo;
    int64_t           i_caching;
};

/*****************************************************************************
 * Open:
 *****************************************************************************/
static int Open( vlc_object_t *p_this )
{
    sout_stream_t       *p_stream = (sout_stream_t*)p_this;
    sout_instance_t     *p_sout = p_stream->p_sout;
    sout_stream_sys_t   *p_sys = NULL;
    config_chain_t      *p_cfg = NULL;
    char                *psz;
    bool          b_rtsp = false;

    config_ChainParse( p_stream, SOUT_CFG_PREFIX,
                       ppsz_sout_options, p_stream->p_cfg );

    p_sys = malloc( sizeof( sout_stream_sys_t ) );
    if( p_sys == NULL )
        return VLC_ENOMEM;

    p_sys->psz_destination = var_GetNonEmptyString( p_stream, SOUT_CFG_PREFIX "dst" );

    p_sys->i_port       = var_GetInteger( p_stream, SOUT_CFG_PREFIX "port" );
    p_sys->i_port_audio = var_GetInteger( p_stream, SOUT_CFG_PREFIX "port-audio" );
    p_sys->i_port_video = var_GetInteger( p_stream, SOUT_CFG_PREFIX "port-video" );
    p_sys->rtcp_mux   = var_GetBool( p_stream, SOUT_CFG_PREFIX "rtcp-mux" );

    p_sys->psz_sdp_file = NULL;

    if( p_sys->i_port_audio && p_sys->i_port_video == p_sys->i_port_audio )
    {
        msg_Err( p_stream, "audio and video RTP port must be distinct" );
        return VLC_EGENERIC;
    }

    for( p_cfg = p_stream->p_cfg; p_cfg != NULL; p_cfg = p_cfg->p_next )
    {
        if( !strcmp( p_cfg->psz_name, "sdp" )
         && ( p_cfg->psz_value != NULL )
         && !strncasecmp( p_cfg->psz_value, "rtsp:", 5 ) )
        {
            b_rtsp = true;
            break;
        }
    }
    if( !b_rtsp )
    {
        psz = var_GetNonEmptyString( p_stream, SOUT_CFG_PREFIX "sdp" );
        if( psz != NULL )
        {
            if( !strncasecmp( psz, "rtsp:", 5 ) )
                b_rtsp = true;
            free( psz );
        }
    }

    /* Transport protocol */
    p_sys->proto = IPPROTO_UDP;
    psz = var_GetNonEmptyString (p_stream, SOUT_CFG_PREFIX"proto");

    if ((psz == NULL) || !strcasecmp (psz, "udp"))
        (void)0; /* default */
    else
    if (!strcasecmp (psz, "dccp"))
    {
        p_sys->proto = IPPROTO_DCCP;
        p_sys->rtcp_mux = true; /* Force RTP/RTCP mux */
    }
#if 0
    else
    if (!strcasecmp (psz, "sctp"))
    {
        p_sys->proto = IPPROTO_TCP;
        p_sys->rtcp_mux = true; /* Force RTP/RTCP mux */
    }
#endif
#if 0
    else
    if (!strcasecmp (psz, "tcp"))
    {
        p_sys->proto = IPPROTO_TCP;
        p_sys->rtcp_mux = true; /* Force RTP/RTCP mux */
    }
#endif
    else
    if (!strcasecmp (psz, "udplite") || !strcasecmp (psz, "udp-lite"))
        p_sys->proto = IPPROTO_UDPLITE;
    else
        msg_Warn (p_this, "unknown or unsupported transport protocol \"%s\"",
                  psz);
    free (psz);
    var_Create (p_this, "dccp-service", VLC_VAR_STRING);

    if( ( p_sys->psz_destination == NULL ) && !b_rtsp )
    {
        msg_Err( p_stream, "missing destination and not in RTSP mode" );
        free( p_sys );
        return VLC_EGENERIC;
    }

    p_sys->i_ttl = var_GetInteger( p_stream, SOUT_CFG_PREFIX "ttl" );
    if( p_sys->i_ttl == -1 )
    {
        /* Normally, we should let the default hop limit up to the core,
         * but we have to know it to build our SDP properly, which is why
         * we ask the core. FIXME: broken when neither sout-rtp-ttl nor
         * ttl are set. */
        p_sys->i_ttl = config_GetInt( p_stream, "ttl" );
    }

    p_sys->b_latm = var_GetBool( p_stream, SOUT_CFG_PREFIX "mp4a-latm" );

    p_sys->payload_bitmap = 0;
    p_sys->i_es = 0;
    p_sys->es   = NULL;
    p_sys->rtsp = NULL;
    p_sys->psz_sdp = NULL;

    p_sys->b_export_sap = false;
    p_sys->b_export_sdp_file = false;
    p_sys->p_session = NULL;

    p_sys->p_httpd_host = NULL;
    p_sys->p_httpd_file = NULL;

    p_stream->p_sys     = p_sys;

    vlc_mutex_init( &p_sys->lock_sdp );
    vlc_mutex_init( &p_sys->lock_es );

    psz = var_GetNonEmptyString( p_stream, SOUT_CFG_PREFIX "mux" );
    if( psz != NULL )
    {
        sout_stream_id_t *id;

        /* Check muxer type */
        if( strncasecmp( psz, "ps", 2 )
         && strncasecmp( psz, "mpeg1", 5 )
         && strncasecmp( psz, "ts", 2 ) )
        {
            msg_Err( p_stream, "unsupported muxer type for RTP (only TS/PS)" );
            free( psz );
            vlc_mutex_destroy( &p_sys->lock_sdp );
            vlc_mutex_destroy( &p_sys->lock_es );
            free( p_sys );
            return VLC_EGENERIC;
        }

        p_sys->p_grab = GrabberCreate( p_stream );
        p_sys->p_mux = sout_MuxNew( p_sout, psz, p_sys->p_grab );
        free( psz );

        if( p_sys->p_mux == NULL )
        {
            msg_Err( p_stream, "cannot create muxer" );
            sout_AccessOutDelete( p_sys->p_grab );
            vlc_mutex_destroy( &p_sys->lock_sdp );
            vlc_mutex_destroy( &p_sys->lock_es );
            free( p_sys );
            return VLC_EGENERIC;
        }

        id = Add( p_stream, NULL );
        if( id == NULL )
        {
            sout_MuxDelete( p_sys->p_mux );
            sout_AccessOutDelete( p_sys->p_grab );
            vlc_mutex_destroy( &p_sys->lock_sdp );
            vlc_mutex_destroy( &p_sys->lock_es );
            free( p_sys );
            return VLC_EGENERIC;
        }

        p_sys->packet = NULL;

        p_stream->pf_add  = MuxAdd;
        p_stream->pf_del  = MuxDel;
        p_stream->pf_send = MuxSend;
    }
    else
    {
        p_sys->p_mux    = NULL;
        p_sys->p_grab   = NULL;

        p_stream->pf_add    = Add;
        p_stream->pf_del    = Del;
        p_stream->pf_send   = Send;
    }

    if( var_GetBool( p_stream, SOUT_CFG_PREFIX"sap" ) )
        SDPHandleUrl( p_stream, "sap" );

    psz = var_GetNonEmptyString( p_stream, SOUT_CFG_PREFIX "sdp" );
    if( psz != NULL )
    {
        config_chain_t *p_cfg;

        SDPHandleUrl( p_stream, psz );

        for( p_cfg = p_stream->p_cfg; p_cfg != NULL; p_cfg = p_cfg->p_next )<commit_msg>stream_out_rtp: fix potential memleak (#2447)<commit_after>/*****************************************************************************
 * rtp.c: rtp stream output module
 *****************************************************************************
 * Copyright (C) 2003-2004 the VideoLAN team
 * Copyright © 2007-2008 <NAME>
 *
 * Authors: <NAME> <<EMAIL>>
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 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 General Public License for more details.
 *
 * You should have received a copy of the GNU 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.
 *****************************************************************************/

/*****************************************************************************
 * Preamble
 *****************************************************************************/

#ifdef HAVE_CONFIG_H
# include "config.h"
#endif

#include <vlc_common.h>
#include <vlc_plugin.h>
#include <vlc_sout.h>
#include <vlc_block.h>

#include <vlc_httpd.h>
#include <vlc_url.h>
#include <vlc_network.h>
#include <vlc_charset.h>
#include <vlc_strings.h>
#include <vlc_rand.h>
#include <srtp.h>

#include "rtp.h"

#ifdef HAVE_UNISTD_H
#   include <sys/types.h>
#   include <unistd.h>
#   include <fcntl.h>
#   include <sys/stat.h>
#endif
#ifdef HAVE_LINUX_DCCP_H
#   include <linux/dccp.h>
#endif
#ifndef IPPROTO_DCCP
# define IPPROTO_DCCP 33
#endif
#ifndef IPPROTO_UDPLITE
# define IPPROTO_UDPLITE 136
#endif

#include <errno.h>

#include <assert.h>

/*****************************************************************************
 * Module descriptor
 *****************************************************************************/

#define DEST_TEXT N_("Destination")
#define DEST_LONGTEXT N_( \
    "This is the output URL that will be used." )
#define SDP_TEXT N_("SDP")
#define SDP_LONGTEXT N_( \
    "This allows you to specify how the SDP (Session Descriptor) for this RTP "\
    "session will be made available. You must use an url: http://location to " \
    "access the SDP via HTTP, rtsp://location for RTSP access, and sap:// " \
    "for the SDP to be announced via SAP." )
#define SAP_TEXT N_("SAP announcing")
#define SAP_LONGTEXT N_("Announce this session with SAP.")
#define MUX_TEXT N_("Muxer")
#define MUX_LONGTEXT N_( \
    "This allows you to specify the muxer used for the streaming output. " \
    "Default is to use no muxer (standard RTP stream)." )

#define NAME_TEXT N_("Session name")
#define NAME_LONGTEXT N_( \
    "This is the name of the session that will be announced in the SDP " \
    "(Session Descriptor)." )
#define DESC_TEXT N_("Session description")
#define DESC_LONGTEXT N_( \
    "This allows you to give a short description with details about the stream, " \
    "that will be announced in the SDP (Session Descriptor)." )
#define URL_TEXT N_("Session URL")
#define URL_LONGTEXT N_( \
    "This allows you to give an URL with more details about the stream " \
    "(often the website of the streaming organization), that will " \
    "be announced in the SDP (Session Descriptor)." )
#define EMAIL_TEXT N_("Session email")
#define EMAIL_LONGTEXT N_( \
    "This allows you to give a contact mail address for the stream, that will " \
    "be announced in the SDP (Session Descriptor)." )
#define PHONE_TEXT N_("Session phone number")
#define PHONE_LONGTEXT N_( \
    "This allows you to give a contact telephone number for the stream, that will " \
    "be announced in the SDP (Session Descriptor)." )

#define PORT_TEXT N_("Port")
#define PORT_LONGTEXT N_( \
    "This allows you to specify the base port for the RTP streaming." )
#define PORT_AUDIO_TEXT N_("Audio port")
#define PORT_AUDIO_LONGTEXT N_( \
    "This allows you to specify the default audio port for the RTP streaming." )
#define PORT_VIDEO_TEXT N_("Video port")
#define PORT_VIDEO_LONGTEXT N_( \
    "This allows you to specify the default video port for the RTP streaming." )

#define TTL_TEXT N_("Hop limit (TTL)")
#define TTL_LONGTEXT N_( \
    "This is the hop limit (also known as \"Time-To-Live\" or TTL) of " \
    "the multicast packets sent by the stream output (-1 = use operating " \
    "system built-in default).")

#define RTCP_MUX_TEXT N_("RTP/RTCP multiplexing")
#define RTCP_MUX_LONGTEXT N_( \
    "This sends and receives RTCP packet multiplexed over the same port " \
    "as RTP packets." )

#define PROTO_TEXT N_("Transport protocol")
#define PROTO_LONGTEXT N_( \
    "This selects which transport protocol to use for RTP." )

#define SRTP_KEY_TEXT N_("SRTP key (hexadecimal)")
#define SRTP_KEY_LONGTEXT N_( \
    "RTP packets will be integrity-protected and ciphered "\
    "with this Secure RTP master shared secret key.")

#define SRTP_SALT_TEXT N_("SRTP salt (hexadecimal)")
#define SRTP_SALT_LONGTEXT N_( \
    "Secure RTP requires a (non-secret) master salt value.")

static const char *const ppsz_protos[] = {
    "dccp", "sctp", "tcp", "udp", "udplite",
};

static const char *const ppsz_protocols[] = {
    "DCCP", "SCTP", "TCP", "UDP", "UDP-Lite",
};

#define RFC3016_TEXT N_("MP4A LATM")
#define RFC3016_LONGTEXT N_( \
    "This allows you to stream MPEG4 LATM audio streams (see RFC3016)." )

static int  Open ( vlc_object_t * );
static void Close( vlc_object_t * );

#define SOUT_CFG_PREFIX "sout-rtp-"
#define MAX_EMPTY_BLOCKS 200

vlc_module_begin ()
    set_shortname( N_("RTP"))
    set_description( N_("RTP stream output") )
    set_capability( "sout stream", 0 )
    add_shortcut( "rtp" )
    set_category( CAT_SOUT )
    set_subcategory( SUBCAT_SOUT_STREAM )

    add_string( SOUT_CFG_PREFIX "dst", "", NULL, DEST_TEXT,
                DEST_LONGTEXT, true );
    add_string( SOUT_CFG_PREFIX "sdp", "", NULL, SDP_TEXT,
                SDP_LONGTEXT, true );
    add_string( SOUT_CFG_PREFIX "mux", "", NULL, MUX_TEXT,
                MUX_LONGTEXT, true );
    add_bool( SOUT_CFG_PREFIX "sap", false, NULL, SAP_TEXT, SAP_LONGTEXT,
              true );

    add_string( SOUT_CFG_PREFIX "name", "", NULL, NAME_TEXT,
                NAME_LONGTEXT, true );
    add_string( SOUT_CFG_PREFIX "description", "", NULL, DESC_TEXT,
                DESC_LONGTEXT, true );
    add_string( SOUT_CFG_PREFIX "url", "", NULL, URL_TEXT,
                URL_LONGTEXT, true );
    add_string( SOUT_CFG_PREFIX "email", "", NULL, EMAIL_TEXT,
                EMAIL_LONGTEXT, true );
    add_string( SOUT_CFG_PREFIX "phone", "", NULL, PHONE_TEXT,
                PHONE_LONGTEXT, true );

    add_string( SOUT_CFG_PREFIX "proto", "udp", NULL, PROTO_TEXT,
                PROTO_LONGTEXT, false );
        change_string_list( ppsz_protos, ppsz_protocols, NULL );
    add_integer( SOUT_CFG_PREFIX "port", 5004, NULL, PORT_TEXT,
                 PORT_LONGTEXT, true );
    add_integer( SOUT_CFG_PREFIX "port-audio", 0, NULL, PORT_AUDIO_TEXT,
                 PORT_AUDIO_LONGTEXT, true );
    add_integer( SOUT_CFG_PREFIX "port-video", 0, NULL, PORT_VIDEO_TEXT,
                 PORT_VIDEO_LONGTEXT, true );

    add_integer( SOUT_CFG_PREFIX "ttl", -1, NULL, TTL_TEXT,
                 TTL_LONGTEXT, true );
    add_bool( SOUT_CFG_PREFIX "rtcp-mux", false, NULL,
              RTCP_MUX_TEXT, RTCP_MUX_LONGTEXT, false );

    add_string( SOUT_CFG_PREFIX "key", "", NULL,
                SRTP_KEY_TEXT, SRTP_KEY_LONGTEXT, false );
    add_string( SOUT_CFG_PREFIX "salt", "", NULL,
                SRTP_SALT_TEXT, SRTP_SALT_LONGTEXT, false );

    add_bool( SOUT_CFG_PREFIX "mp4a-latm", 0, NULL, RFC3016_TEXT,
                 RFC3016_LONGTEXT, false );

    set_callbacks( Open, Close )
vlc_module_end ()

/*****************************************************************************
 * Exported prototypes
 *****************************************************************************/
static const char *const ppsz_sout_options[] = {
    "dst", "name", "port", "port-audio", "port-video", "*sdp", "ttl", "mux",
    "sap", "description", "url", "email", "phone",
    "proto", "rtcp-mux", "key", "salt",
    "mp4a-latm", NULL
};

static sout_stream_id_t *Add ( sout_stream_t *, es_format_t * );
static int               Del ( sout_stream_t *, sout_stream_id_t * );
static int               Send( sout_stream_t *, sout_stream_id_t *,
                               block_t* );
static sout_stream_id_t *MuxAdd ( sout_stream_t *, es_format_t * );
static int               MuxDel ( sout_stream_t *, sout_stream_id_t * );
static int               MuxSend( sout_stream_t *, sout_stream_id_t *,
                                  block_t* );

static sout_access_out_t *GrabberCreate( sout_stream_t *p_sout );
static void* ThreadSend( vlc_object_t *p_this );

static void SDPHandleUrl( sout_stream_t *, const char * );

static int SapSetup( sout_stream_t *p_stream );
static int FileSetup( sout_stream_t *p_stream );
static int HttpSetup( sout_stream_t *p_stream, const vlc_url_t * );

struct sout_stream_sys_t
{
    /* SDP */
    char    *psz_sdp;
    vlc_mutex_t  lock_sdp;

    /* SDP to disk */
    bool b_export_sdp_file;
    char *psz_sdp_file;

    /* SDP via SAP */
    bool b_export_sap;
    session_descriptor_t *p_session;

    /* SDP via HTTP */
    httpd_host_t *p_httpd_host;
    httpd_file_t *p_httpd_file;

    /* RTSP */
    rtsp_stream_t *rtsp;

    /* */
    char     *psz_destination;
    uint32_t  payload_bitmap;
    uint16_t  i_port;
    uint16_t  i_port_audio;
    uint16_t  i_port_video;
    uint8_t   proto;
    bool      rtcp_mux;
    int       i_ttl:9;
    bool      b_latm;

    /* in case we do TS/PS over rtp */
    sout_mux_t        *p_mux;
    sout_access_out_t *p_grab;
    block_t           *packet;

    /* */
    vlc_mutex_t      lock_es;
    int              i_es;
    sout_stream_id_t **es;
};

typedef int (*pf_rtp_packetizer_t)( sout_stream_id_t *, block_t * );

typedef struct rtp_sink_t
{
    int rtp_fd;
    rtcp_sender_t *rtcp;
} rtp_sink_t;

struct sout_stream_id_t
{
    VLC_COMMON_MEMBERS

    sout_stream_t *p_stream;
    /* rtp field */
    uint16_t    i_sequence;
    uint8_t     i_payload_type;
    uint8_t     ssrc[4];

    /* for sdp */
    const char  *psz_enc;
    char        *psz_fmtp;
    int          i_clock_rate;
    int          i_port;
    int          i_cat;
    int          i_channels;
    int          i_bitrate;

    /* Packetizer specific fields */
    int                 i_mtu;
    srtp_session_t     *srtp;
    pf_rtp_packetizer_t pf_packetize;

    /* Packets sinks */
    vlc_mutex_t       lock_sink;
    int               sinkc;
    rtp_sink_t       *sinkv;
    rtsp_stream_id_t *rtsp_id;
    int              *listen_fd;

    block_fifo_t     *p_fifo;
    int64_t           i_caching;
};

/*****************************************************************************
 * Open:
 *****************************************************************************/
static int Open( vlc_object_t *p_this )
{
    sout_stream_t       *p_stream = (sout_stream_t*)p_this;
    sout_instance_t     *p_sout = p_stream->p_sout;
    sout_stream_sys_t   *p_sys = NULL;
    config_chain_t      *p_cfg = NULL;
    char                *psz;
    bool          b_rtsp = false;

    config_ChainParse( p_stream, SOUT_CFG_PREFIX,
                       ppsz_sout_options, p_stream->p_cfg );

    p_sys = malloc( sizeof( sout_stream_sys_t ) );
    if( p_sys == NULL )
        return VLC_ENOMEM;

    p_sys->psz_destination = var_GetNonEmptyString( p_stream, SOUT_CFG_PREFIX "dst" );

    p_sys->i_port       = var_GetInteger( p_stream, SOUT_CFG_PREFIX "port" );
    p_sys->i_port_audio = var_GetInteger( p_stream, SOUT_CFG_PREFIX "port-audio" );
    p_sys->i_port_video = var_GetInteger( p_stream, SOUT_CFG_PREFIX "port-video" );
    p_sys->rtcp_mux     = var_GetBool( p_stream, SOUT_CFG_PREFIX "rtcp-mux" );

    p_sys->psz_sdp_file = NULL;

    if( p_sys->i_port_audio && p_sys->i_port_video == p_sys->i_port_audio )
    {
        msg_Err( p_stream, "audio and video RTP port must be distinct" );
        free( p_sys->psz_destination );
        free( p_sys );
        return VLC_EGENERIC;
    }

    for( p_cfg = p_stream->p_cfg; p_cfg != NULL; p_cfg = p_cfg->p_next )
    {
        if( !strcmp( p_cfg->psz_name, "sdp" )
         && ( p_cfg->psz_value != NULL )
         && !strncasecmp( p_cfg->psz_value, "rtsp:", 5 ) )
        {
            b_rtsp = true;
            break;
        }
    }
    if( !b_rtsp )
    {
        psz = var_GetNonEmptyString( p_stream, SOUT_CFG_PREFIX "sdp" );
        if( psz != NULL )
        {
            if( !strncasecmp( psz, "rtsp:", 5 ) )
                b_rtsp = true;
            free( psz );
        }
    }

    /* Transport protocol */
    p_sys->proto = IPPROTO_UDP;
    psz = var_GetNonEmptyString (p_stream, SOUT_CFG_PREFIX"proto");

    if ((psz == NULL) || !strcasecmp (psz, "udp"))
        (void)0; /* default */
    else
    if (!strcasecmp (psz, "dccp"))
    {
        p_sys->proto = IPPROTO_DCCP;
        p_sys->rtcp_mux = true; /* Force RTP/RTCP mux */
    }
#if 0
    else
    if (!strcasecmp (psz, "sctp"))
    {
        p_sys->proto = IPPROTO_TCP;
        p_sys->rtcp_mux = true; /* Force RTP/RTCP mux */
    }
#endif
#if 0
    else
    if (!strcasecmp (psz, "tcp"))
    {
        p_sys->proto = IPPROTO_TCP;
        p_sys->rtcp_mux = true; /* Force RTP/RTCP mux */
    }
#endif
    else
    if (!strcasecmp (psz, "udplite") || !strcasecmp (psz, "udp-lite"))
        p_sys->proto = IPPROTO_UDPLITE;
    else
        msg_Warn (p_this, "unknown or unsupported transport protocol \"%s\"",
                  psz);
    free (psz);
    var_Create (p_this, "dccp-service", VLC_VAR_STRING);

    if( ( p_sys->psz_destination == NULL ) && !b_rtsp )
    {
        msg_Err( p_stream, "missing destination and not in RTSP mode" );
        free( p_sys );
        return VLC_EGENERIC;
    }

    p_sys->i_ttl = var_GetInteger( p_stream, SOUT_CFG_PREFIX "ttl" );
    if( p_sys->i_ttl == -1 )
    {
        /* Normally, we should let the default hop limit up to the core,
         * but we have to know it to build our SDP properly, which is why
         * we ask the core. FIXME: broken when neither sout-rtp-ttl nor
         * ttl are set. */
        p_sys->i_ttl = config_GetInt( p_stream, "ttl" );
    }

    p_sys->b_latm = var_GetBool( p_stream, SOUT_CFG_PREFIX "mp4a-latm" );

    p_sys->payload_bitmap = 0;
    p_sys->i_es = 0;
    p_sys->es   = NULL;
    p_sys->rtsp = NULL;
    p_sys->psz_sdp = NULL;

    p_sys->b_export_sap = false;
    p_sys->b_export_sdp_file = false;
    p_sys->p_session = NULL;

    p_sys->p_httpd_host = NULL;
    p_sys->p_httpd_file = NULL;

    p_stream->p_sys     = p_sys;

    vlc_mutex_init( &p_sys->lock_sdp );
    vlc_mutex_init( &p_sys->lock_es );

    psz = var_GetNonEmptyString( p_stream, SOUT_CFG_PREFIX "mux" );
    if( psz != NULL )
    {
        sout_stream_id_t *id;

        /* Check muxer type */
        if( strncasecmp( psz, "ps", 2 )
         && strncasecmp( psz, "mpeg1", 5 )
         && strncasecmp( psz, "ts", 2 ) )
        {
            msg_Err( p_stream, "unsupported muxer type for RTP (only TS/PS)" );
            free( psz );
            vlc_mutex_destroy( &p_sys->lock_sdp );
            vlc_mutex_destroy( &p_sys->lock_es );
            free( p_sys->psz_destination );
            free( p_sys );
            return VLC_EGENERIC;
        }

        p_sys->p_grab = GrabberCreate( p_stream );
        p_sys->p_mux = sout_MuxNew( p_sout, psz, p_sys->p_grab );
        free( psz );

        if( p_sys->p_mux == NULL )
        {
            msg_Err( p_stream, "cannot create muxer" );
            sout_AccessOutDelete( p_sys->p_grab );
            vlc_mutex_destroy( &p_sys->lock_sdp );
            vlc_mutex_destroy( &p_sys->lock_es );
            free( p_sys->psz_destination );
            free( p_sys );
            return VLC_EGENERIC;
        }

        id = Add( p_stream, NULL );
        if( id == NULL )
        {
            sout_MuxDelete( p_sys->p_mux );
            sout_AccessOutDelete( p_sys->p_grab );
            vlc_mutex_destroy( &p_sys->lock_sdp );
            vlc_mutex_destroy( &p_sys->lock_es );
            free( p_sys->psz_destination );
            free( p_sys );
            return VLC_EGENERIC;
        }

        p_sys->packet = NULL;

        p_stream->pf_add  = MuxAdd;
        p_stream->pf_del  = MuxDel;
        p_stream->pf_send = MuxSend;
    }
    else
    {
        p_sys->p_mux    = NULL;
        p_sys->p_grab   = NULL;

        p_stream->pf_add    = Add;
        p_stream->pf_del    = Del;
        p_stream->pf_send   = Send;
    }

    if( var_GetBool( p_stream, SOUT_CFG_PREFIX"sap" ) )
        SDPHandleUrl( p_stream, "sap" );

    psz = var_GetNonEmptyString( p_stream, SOUT_CFG_PREFIX "sdp" );
    if( psz != NULL )
    {
        config_chain_t *p_cfg;

        SDPHandleUrl( p_stream, psz );

        for( p_cfg = p_stream->p_cfg; p_cfg != NULL; p_cfg = p_cfg->p_next )
{'fraction_non_alphanumeric': 0.15425980292416358, 'fraction_numerical': 0.005929715431793739, 'mean_word_length': 2.749346120313862, 'pattern_counts': {'":': 0, '<': 47, '<?xml version=': 0, '>': 183, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 2, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '1427054', 'n_tokens_mistral': 12177, 'n_tokens_neox': 12000, 'n_words': 2996}