]> git.sesse.net Git - vlc/commitdiff
* ALL: Better announce system
authorClément Stenac <zorglub@videolan.org>
Sun, 18 Apr 2004 18:21:09 +0000 (18:21 +0000)
committerClément Stenac <zorglub@videolan.org>
Sun, 18 Apr 2004 18:21:09 +0000 (18:21 +0000)
  - The SAP handler now runs in a separate thread.
  - RTP sessions can be announced with sap (sdp=sap://,name=...)
TODO: Make this more configurable
  - Better SDP generation (the timestamp problem is not resolved)
About this, there is a problem : as, for a RTP session, the URI
        is the complete SDP, if the session is recreated, as the URI has
        changed, a new item is added to the playlist
  - Experimental flow control algorithm :
       It does not follow the "Recommended" implementation, as it needs
       to count the sessions (to achieve this, we should make this work
       together with the SAP listener)
       It is disabled by default (use --sap-flow-control to enable).
       When it is disabled, sap announcement interval is set by --sap-interval

* src/misc/net.c : created net_ReadNonBlock
* sap.c : Fixed memory problem

18 files changed:
Makefile.am
NEWS
include/network.h
include/stream_output.h
include/vlc/vlc.h
include/vlc_common.h
include/vlc_objects.h
modules/misc/sap.c
modules/stream_out/announce.c
modules/stream_out/announce.h
modules/stream_out/rtp.c
modules/stream_out/standard.c
src/libvlc.c
src/libvlc.h
src/misc/net.c
src/misc/objects.c
src/stream_output/announce.c [new file with mode: 0644]
src/stream_output/sap.c [new file with mode: 0644]

index 99addfe7e9da39bee98ff76fce405086ee5a3354..86a8a751d79da3bc30167702caccfeb54fc0aed9 100644 (file)
@@ -348,6 +348,8 @@ SOURCES_libvlc_common = \
        src/audio_output/output.c \
        src/audio_output/intf.c \
        src/stream_output/stream_output.c \
+       src/stream_output/announce.c \
+       src/stream_output/sap.c \
        src/misc/charset.c \
        src/misc/httpd.c \
        src/misc/mtime.c \
diff --git a/NEWS b/NEWS
index e693d427a14fbd0bb6e58b21e61d136261816c7f..d248ecf5df7b510d1369ad573f712bb96cbec317 100644 (file)
--- a/NEWS
+++ b/NEWS
@@ -1,4 +1,29 @@
-$Id: NEWS,v 1.86 2004/03/02 23:49:38 hartman Exp $
+$Id$
+
+Changes between 0.7.1 and 0.7.2:
+--------------------------------
+
+Core support:
+ * Bookmarks feature
+ * Support for video output embedded in interfaces
+
+Codecs:
+
+Playlist:
+
+Input:
+
+Stream output:
+ * Improved session announcement system
+
+Interfaces:
+ * Skins
+    - Ability to embed video output
+
+ * wxWindows
+    - new design and set of icons
+    - new streaming wizard
+    - Ability to embed video output
 
 Changes between 0.7.1 and 0.7.1a:
 ---------------------------------
index 0b8a8c87fcc2674a9f95f106e3e43d67876621f7..d44157e2c89158bd3eadb6730186689310f9007f 100644 (file)
@@ -2,7 +2,7 @@
  * network.h: interface to communicate with network plug-ins
  *****************************************************************************
  * Copyright (C) 2002 VideoLAN
- * $Id: network.h,v 1.10 2004/01/21 10:22:31 fenrir Exp $
+ * $Id$
  *
  * Authors: Christophe Massiot <massiot@via.ecp.fr>
  *          Laurent Aimar <fenrir@via.ecp.fr>
@@ -173,6 +173,9 @@ VLC_EXPORT( void, net_Close, ( int fd ) );
 #define net_Read(a,b,c,d,e) __net_Read(VLC_OBJECT(a),b,c,d,e)
 VLC_EXPORT( int, __net_Read, ( vlc_object_t *p_this, int fd, uint8_t *p_data, int i_data, vlc_bool_t b_retry ) );
 
+#define net_ReadNonBlock(a,b,c,d,e) __net_ReadNonBlock(VLC_OBJECT(a),b,c,d,e)
+VLC_EXPORT( int, __net_ReadNonBlock, ( vlc_object_t *p_this, int fd, uint8_t *p_data, int i_data, mtime_t i_wait ) );
+
 #define net_Write(a,b,c,d) __net_Write(VLC_OBJECT(a),b,c,d)
 VLC_EXPORT( int, __net_Write, ( vlc_object_t *p_this, int fd, uint8_t *p_data, int i_data ) );
 
index 26c4e0242013e65a60eb850ded010f26a9d23cc6..d48709159c999a96f9ea0fc86b573a67ad5e7e6b 100644 (file)
@@ -181,6 +181,108 @@ struct sout_instance_t
     sout_instance_sys_t *p_sys;
 };
 
+
+/* Announce handler structures */
+
+/* Session and method descriptors */
+
+struct sap_session_t;
+
+struct session_descriptor_t
+{
+    char *psz_name;
+    char *psz_uri;
+    int i_port;
+    int i_ttl;
+    int i_payload;   /* SAP Payload type */
+
+    sap_session_t *p_sap; /* If we have a sap session, remember it */
+    char *psz_sdp;
+};
+
+#define METHOD_TYPE_SAP 1
+#define METHOD_TYPE_SLP 2
+
+struct announce_method_t
+{
+    int i_type;
+
+    /* For SAP */
+    int i_ip_version;
+    char *psz_ipv6_scope;
+    char *psz_address; /* If we use a custom address */
+};
+
+
+/* SAP Specific structures */
+
+/* 100ms */
+#define SAP_IDLE ((mtime_t)(0.100*CLOCK_FREQ))
+#define SAP_MAX_BUFFER 65534
+#define MIN_INTERVAL 2
+#define MAX_INTERVAL 300
+
+/* A SAP announce address. For each of these, we run the
+ * control flow algorithm */
+struct sap_address_t
+{
+    char *psz_address;
+    int i_port;
+    int i_rfd; /* Read socket */
+    int i_wfd; /* Write socket */
+
+    /* Used for flow control */
+    mtime_t t1;
+    vlc_bool_t b_enabled;
+    vlc_bool_t b_ready;
+    int i_interval;
+    int i_buff;
+    int i_limit;
+};
+
+/* A SAP session descriptor, enqueued in the SAP handler queue */
+struct sap_session_t
+{
+    char          *psz_sdp;
+    char          *psz_data;
+    int            i_length;
+    sap_address_t *p_address;
+
+    /* Last and next send */
+    mtime_t        i_last;
+    mtime_t        i_next;
+};
+
+/* The SAP handler, running in a separate thread */
+struct sap_handler_t
+{
+    VLC_COMMON_MEMBERS /* needed to create a thread */
+
+    sap_session_t **pp_sessions;
+    sap_address_t **pp_addresses;
+
+    vlc_bool_t b_control;
+
+    int i_sessions;
+    int i_addresses;
+
+    int i_current_session;
+
+    int (*pf_add)  ( sap_handler_t*, session_descriptor_t *,announce_method_t*);
+    int (*pf_del)  ( sap_handler_t*, session_descriptor_t *);
+};
+
+/* The main announce handler object */
+struct announce_handler_t
+{
+    VLC_COMMON_MEMBERS
+
+    sap_handler_t *p_sap;
+};
+
+/* End */
+
+
 static inline sout_cfg_t *sout_cfg_find( sout_cfg_t *p_cfg, char *psz_name )
 {
     while( p_cfg && strcmp( p_cfg->psz_name, psz_name ) )
@@ -205,6 +307,9 @@ static inline char *sout_cfg_find_value( sout_cfg_t *p_cfg, char *psz_name )
 
     return NULL;
 }
+
+
+
 /*****************************************************************************
  * Prototypes
  *****************************************************************************/
@@ -232,3 +337,25 @@ VLC_EXPORT( char *,             sout_cfg_parser, ( char **, sout_cfg_t **, char
 VLC_EXPORT( sout_stream_t *,    sout_stream_new, ( sout_instance_t *, char *psz_chain ) );
 VLC_EXPORT( void,               sout_stream_delete, ( sout_stream_t *p_stream ) );
 
+/* Announce system */
+VLC_EXPORT( int,                sout_AnnounceRegister, (sout_instance_t *,session_descriptor_t*, announce_method_t* ) );
+VLC_EXPORT(session_descriptor_t*,sout_AnnounceRegisterSDP, (sout_instance_t *,char *, announce_method_t* ) );
+VLC_EXPORT( int,                sout_AnnounceUnRegister, (sout_instance_t *,session_descriptor_t* ) );
+
+VLC_EXPORT(session_descriptor_t*,sout_AnnounceSessionCreate, () );
+VLC_EXPORT(void,                 sout_AnnounceSessionDestroy, (session_descriptor_t *) );
+VLC_EXPORT(announce_method_t*,   sout_AnnounceMethodCreate, (int) );
+
+#define announce_HandlerCreate(a) __announce_HandlerCreate(VLC_OBJECT(a))
+announce_handler_t*  __announce_HandlerCreate( vlc_object_t *);
+
+/* Private functions for the announce handler */
+int announce_HandlerDestroy( announce_handler_t * );
+int announce_Register( announce_handler_t *p_announce,
+                session_descriptor_t *p_session,
+                announce_method_t *p_method );
+int announce_UnRegister( announce_handler_t *p_announce,
+                session_descriptor_t *p_session );
+
+sap_handler_t *announce_SAPHandlerCreate( announce_handler_t *p_announce );
+void announce_SAPHandlerDestroy( sap_handler_t *p_sap );
index dc1200458c5eacab31d9e6637bd47f0191c8b5b5..3017b3ad10217d1b54e834c82c8fb94e59131193 100644 (file)
@@ -2,7 +2,7 @@
  * vlc.h: global header for vlc
  *****************************************************************************
  * Copyright (C) 1998, 1999, 2000 VideoLAN
- * $Id: vlc.h,v 1.30 2004/01/25 18:17:08 zorglub Exp $
+ * $Id$
  *
  * 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
  * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111, USA.
  *****************************************************************************/
 
+/**
+ * \defgroup libvlc Libvlc
+ * This is libvlc.
+ *
+ * @{
+ */
+
+
 #ifndef _VLC_VLC_H
 #define _VLC_VLC_H 1
 
@@ -33,6 +41,9 @@ typedef int vlc_bool_t;
 typedef struct vlc_list_t vlc_list_t;
 typedef struct vlc_object_t vlc_object_t;
 
+/**
+ * VLC value structure
+ */
 typedef union
 {
     int             i_int;
@@ -56,6 +67,9 @@ typedef union
 
 } vlc_value_t;
 
+/**
+ * VLC list structure
+ */
 struct vlc_list_t
 {
     int             i_count;
@@ -102,7 +116,9 @@ struct vlc_list_t
 
 #define PLAYLIST_END           -666
 
-/** Playlist commands */
+/**
+ * Playlist commands
+ */
 typedef enum {
     PLAYLIST_PLAY,                              /**< Starts playing. No arg. */
     PLAYLIST_PAUSE,                     /**< Toggles playlist pause. No arg. */
@@ -122,16 +138,94 @@ typedef enum {
 /*****************************************************************************
  * Exported libvlc API
  *****************************************************************************/
+
+/**
+ * Retrieve libvlc version
+ *
+ * \return a string containing the libvlc version
+ */
 char const * VLC_Version ( void );
-char const * VLC_Error   ( int );
 
+/**
+ * Return an error string
+ *
+ * \param i_err an error code
+ * \return an error string
+ */
+char const * VLC_Error ( int i_err );
+
+/**
+ * Initialize libvlc
+ *
+ * This function allocates a vlc_t structure and returns a negative value
+ * in case of failure. Also, the thread system is initialized
+ *
+ * \return vlc object id or an error code
+ */
 int     VLC_Create       ( void );
+
+/**
+ * Initialize a vlc_t structure
+ *
+ * This function initializes a previously allocated vlc_t structure:
+ *  - CPU detection
+ *  - gettext initialization
+ *  - message queue, module bank and playlist initialization
+ *  - configuration and commandline parsing
+ *
+ *  \param i_object a vlc object id
+ *  \param i_argc the number of arguments
+ *  \param ppsz_argv an array of arguments
+ *  \return VLC_SUCCESS on success
+ */
 int     VLC_Init         ( int, int, char *[] );
+
+/**
+ * Ask vlc to die
+ *
+ * This function sets p_vlc->b_die to VLC_TRUE, but does not do any other
+ * task. It is your duty to call VLC_End and VLC_Destroy afterwards.
+ *
+ * \param i_object a vlc object id
+ * \return VLC_SUCCESS on success
+ */
 int     VLC_Die          ( int );
+
+/**
+ * Stop playing and destroy everything.
+ *
+ * This function requests the running threads to finish, waits for their
+ * termination, and destroys their structure.
+ * \param i_object a vlc object id
+ * \return VLC_SUCCESS on success
+ */
 int     VLC_Destroy      ( int );
 
+/**
+ * Set a VLC variable
+ *
+ * This function sets a variable of VLC
+ *
+ * \param i_object a vlc object id
+ * \param psz_var a vlc variable name
+ * \param value a vlc_value_t structure
+ * \return VLC_SUCCESS on success
+ */
 int     VLC_Set          ( int, char const *, vlc_value_t );
+
+/**
+ * Get a VLC variable
+ *
+ * This function gets the value of a variable of VLC
+ * It stores it in the p_value argument
+ *
+ * \param i_object a vlc object id
+ * \param psz_var a vlc variable name
+ * \param p_value a pointer to a vlc_value_t structure
+ * \return VLC_SUCCESS on success
+ */
 int     VLC_Get          ( int, char const *, vlc_value_t * );
+
 int     VLC_AddIntf      ( int, char const *, vlc_bool_t );
 int     VLC_AddTarget    ( int, char const *, const char **, int, int, int );
 
index 0d32ea6a88f119173b4a9f89b85c0c2e8866da06..9702491b78a4cb222f44cccb52a905e3765ca792 100644 (file)
@@ -272,8 +272,13 @@ typedef struct sout_access_out_t sout_access_out_t;
 typedef struct sout_mux_t sout_mux_t;
 typedef struct sout_stream_t    sout_stream_t;
 typedef struct sout_cfg_t       sout_cfg_t;
-/*typedef struct sap_session_t    sap_session_t;
-typedef struct slp_session_t    slp_session_t;*/
+typedef struct sap_session_t    sap_session_t;
+typedef struct sap_address_t sap_address_t;
+typedef struct session_descriptor_t session_descriptor_t;
+typedef struct announce_method_t announce_method_t;
+typedef struct announce_handler_t announce_handler_t;
+typedef struct sap_handler_t sap_handler_t;
+//typedef struct slp_session_t    slp_session_t;
 
 /* Decoders */
 typedef struct decoder_t      decoder_t;
index e171d0780a17dd2e8e671bcd1e654e8cddb66395..c83c1a2b26d9bc80cc114e2ce9dbd1cd1bb915e8 100644 (file)
@@ -48,6 +48,7 @@
 #define VLC_OBJECT_ENCODER   (-14)
 #define VLC_OBJECT_DIALOGS   (-15)
 #define VLC_OBJECT_VLM       (-16)
+#define VLC_OBJECT_ANNOUNCE (-17)
 
 #define VLC_OBJECT_GENERIC  (-666)
 
index 73072db08060d5404ef89a97d4d3bb24740ebe4f..f4d145aaae8360c8b2c3bd1b58fa071178b4603b 100644 (file)
@@ -1,4 +1,3 @@
-
 /*****************************************************************************
  * sap.c :  SAP interface module
  *****************************************************************************
@@ -527,6 +526,7 @@ static void Run( intf_thread_t *p_intf )
             msg_Warn( p_intf, "ditching sap packet" );
         }
 
+        memset( buffer, 0, MAX_SAP_BUFFER );
     }
 }
 
@@ -972,7 +972,7 @@ static sess_descr_t *  parse_sdp( intf_thread_t * p_intf, char *p_packet )
 
         if( p_packet[1] != '=' )
         {
-            msg_Warn( p_intf, "packet invalid" );
+            msg_Warn( p_intf, "invalid packet") ;
             free_sd( sd );
             return NULL;
         }
index 888c705415b5cdf89426e9d0000c0ae9bc9201fd..aaf6fc41d4d4ef7c508895c6fd073290953ced57 100644 (file)
@@ -138,12 +138,12 @@ char * SDPGenerateUDP(char * psz_name_arg,char * psz_url_arg)
 /*****************************************************************************
  * sout_SAPNew: Creates a SAP Session
  *****************************************************************************/
-sap_session_t * sout_SAPNew ( sout_instance_t *p_sout,
+sap_session_t2 * sout_SAPNew ( sout_instance_t *p_sout,
                                      char * psz_sdp_arg,
                                      int ip_version,
                                      char * psz_v6_scope )
 {
-    sap_session_t       *p_sap; /* The SAP structure */
+    sap_session_t2       *p_sap; /* The SAP structure */
     char                *sap_ipv6_addr = NULL; /* IPv6 built address */
     vlc_value_t         val;
 
@@ -151,7 +151,7 @@ sap_session_t * sout_SAPNew ( sout_instance_t *p_sout,
     var_Create( p_sout, "ipv4", VLC_VAR_BOOL );
 
     /* Allocate the SAP structure */
-    p_sap = (sap_session_t *) malloc( sizeof ( sap_session_t ) ) ;
+    p_sap = (sap_session_t2 *) malloc( sizeof ( sap_session_t2 ) ) ;
     if ( !p_sap )
     {
         msg_Err( p_sout, "out of memory" );
@@ -214,7 +214,7 @@ sap_session_t * sout_SAPNew ( sout_instance_t *p_sout,
 /*****************************************************************************
  * sout_SAPDelete: Deletes a SAP Session
  *****************************************************************************/
-void sout_SAPDelete( sout_instance_t *p_sout, sap_session_t * p_sap )
+void sout_SAPDelete( sout_instance_t *p_sout, sap_session_t2 * p_sap )
 {
     int i_ret;
 
@@ -243,7 +243,7 @@ void sout_SAPDelete( sout_instance_t *p_sout, sap_session_t * p_sap )
 /*****************************************************************************
  * sout_SAPSend: Sends a SAP packet
  *****************************************************************************/
-void sout_SAPSend( sout_instance_t *p_sout, sap_session_t * p_sap )
+void sout_SAPSend( sout_instance_t *p_sout, sap_session_t2 * p_sap )
 {
     char *psz_msg;                     /* SDP content */
     char *psz_head;                         /* SAP header */
index e89e1a21ec1dc6dedcd94a7afac1c52e2df41ccd..342adbda7ffbf6e449c5afb6e9df8d6114473107 100644 (file)
@@ -51,7 +51,7 @@
 /*****************************************************************************
  * sap_session_t: SAP Session descriptor
  *****************************************************************************/
-struct sap_session_t
+struct sap_session_t2
 {
         char * psz_sdp;
         module_t p_network;
@@ -60,7 +60,7 @@ struct sap_session_t
         int i_ip_version;
 };
 
-typedef struct sap_session_t sap_session_t;
+typedef struct sap_session_t2 sap_session_t2;
 /*****************************************************************************
  * slp_session_t: SLP Session descriptor
  *****************************************************************************/
@@ -75,12 +75,13 @@ typedef struct slp_session_t slp_session_t;
 /*****************************************************************************
  * Prototypes
  *****************************************************************************/
+sap_session_t2 *  sout_SAPNew        (sout_instance_t *, char * psz_sdp_arg,
+                                                      int ip_version,
+                                                      char * psz_v6_scope );
 
 char * SDPGenerateUDP(char * ,char *);
-sap_session_t *  sout_SAPNew        (sout_instance_t *,
-                                     char* , int, char *);
-void             sout_SAPDelete     (sout_instance_t *,sap_session_t*);
-void             sout_SAPSend       (sout_instance_t *,sap_session_t *);
+void             sout_SAPDelete     (sout_instance_t *,sap_session_t2*);
+void             sout_SAPSend       (sout_instance_t *,sap_session_t2 *);
 int              sout_SLPReg        (sout_instance_t *,char *,char *);
 int              sout_SLPDereg      (sout_instance_t *,char *,char *);
 
index 557a6999315ffc6d4be20fdc0cf315312187e96d..3440c2afdce25b5eee86c12e769252963eb63b82 100644 (file)
@@ -76,9 +76,14 @@ struct sout_stream_sys_t
     int64_t i_sdp_id;
     int     i_sdp_version;
     char    *psz_sdp;
-
     vlc_mutex_t  lock_sdp;
 
+    char        *psz_session_name;
+
+    /* sap */
+    vlc_bool_t b_export_sap;
+    session_descriptor_t *p_session;
+
     httpd_host_t *p_httpd_host;
     httpd_file_t *p_httpd_file;
 
@@ -155,6 +160,7 @@ struct sout_stream_id_t
 
 static int AccessOutGrabberWrite( sout_access_out_t *, block_t * );
 
+static int SapSetup( sout_stream_t *p_stream );
 static int HttpSetup( sout_stream_t *p_stream, vlc_url_t * );
 static int RtspSetup( sout_stream_t *p_stream, vlc_url_t * );
 
@@ -181,6 +187,7 @@ static int Open( vlc_object_t *p_this )
 
     p_sys = malloc( sizeof( sout_stream_sys_t ) );
     p_sys->psz_destination = sout_cfg_find_value( p_stream->p_cfg, "dst" );
+    p_sys->psz_session_name = sout_cfg_find_value( p_stream->p_cfg, "name" );
     if( ( val = sout_cfg_find_value( p_stream->p_cfg, "port" ) ) )
     {
         p_sys->i_port = atoi( val );
@@ -190,6 +197,18 @@ static int Open( vlc_object_t *p_this )
         p_sys->i_port = 1234;
     }
 
+    if( !p_sys->psz_session_name )
+    {
+        if( p_sys->psz_destination )
+        {
+            p_sys->psz_session_name = strdup( p_sys->psz_destination );
+        }
+        else
+        {
+           p_sys->psz_session_name = strdup( "NONE" );
+        }
+    }
+
     if( !p_sys->psz_destination || *p_sys->psz_destination == '\0' )
     {
         val = sout_cfg_find_value( p_stream->p_cfg, "sdp" );
@@ -227,6 +246,10 @@ static int Open( vlc_object_t *p_this )
     p_sys->i_sdp_id = mdate();
     p_sys->i_sdp_version = 1;
     p_sys->psz_sdp = NULL;
+
+    p_sys->b_export_sap = VLC_FALSE;
+    p_sys->p_session = NULL;
+
     p_sys->p_httpd_host = NULL;
     p_sys->p_httpd_file = NULL;
     p_sys->p_rtsp_host  = NULL;
@@ -320,15 +343,15 @@ static int Open( vlc_object_t *p_this )
         sprintf( p_sys->psz_sdp,
                  "v=0\n"
                  "o=- "I64Fd" %d IN IP4 127.0.0.1\n"
-                 "s=NONE\n"
+                 "s=%s\n"
                  "c=IN IP4 %s/%d\n"
                  "m=video %d RTP/AVP %d\n"
                  "a=rtpmap:%d %s\n",
                  p_sys->i_sdp_id, p_sys->i_sdp_version,
+                 p_sys->psz_session_name,
                  p_sys->psz_destination, p_sys->i_ttl,
                  p_sys->i_port, p_sys->i_payload_type,
                  p_sys->i_payload_type, psz_rtpmap );
-
         fprintf( stderr, "sdp=%s", p_sys->psz_sdp );
 
         /* create the rtp context */
@@ -367,6 +390,11 @@ static int Open( vlc_object_t *p_this )
                 msg_Err( p_stream, "cannot export sdp as rtsp" );
             }
         }
+        else if( url.psz_protocol && !strcasecmp( url.psz_protocol, "sap" ) )
+        {
+            p_sys->b_export_sap = VLC_TRUE;
+            SapSetup( p_stream );
+        }
         else
         {
             msg_Warn( p_stream, "unknow protocol for SDP (%s)",
@@ -426,7 +454,13 @@ static void Close( vlc_object_t * p_this )
     {
         httpd_HostDelete( p_sys->p_rtsp_host );
     }
-
+#if 0
+    if( p_sys->psz_session_name )
+    {
+        free( p_sys->psz_session_name );
+        p_sys->psz_session_name = NULL;
+    }
+#endif
     if( p_sys->psz_sdp )
     {
         free( p_sys->psz_sdp );
@@ -446,9 +480,10 @@ static char *SDPGenerate( sout_stream_t *p_stream, char *psz_destination, vlc_bo
 
     i_size = strlen( "v=0\n" ) +
              strlen( "o=- * * IN IP4 127.0.0.1\n" ) +
-             strlen( "s=NONE\n" ) +
+             strlen( "s=\n" ) +
              strlen( "c=IN IP4 */*\n" ) +
              strlen( psz_destination ? psz_destination : "0.0.0.0") +
+             strlen( p_sys->psz_session_name ) +
              20 + 10 + 10 + 1;
     for( i = 0; i < p_sys->i_es; i++ )
     {
@@ -473,7 +508,7 @@ static char *SDPGenerate( sout_stream_t *p_stream, char *psz_destination, vlc_bo
     p += sprintf( p, "v=0\n" );
     p += sprintf( p, "o=- "I64Fd" %d IN IP4 127.0.0.1\n",
                   p_sys->i_sdp_id, p_sys->i_sdp_version );
-    p += sprintf( p, "s=NONE\n" );
+    p += sprintf( p, "s=%s\n", p_sys->psz_session_name );
     p += sprintf( p, "c=IN IP4 %s/%d\n", psz_destination ? psz_destination : "0.0.0.0",
                   p_sys->i_ttl );
 
@@ -746,6 +781,12 @@ static sout_stream_id_t *Add( sout_stream_t *p_stream, es_format_t *p_fmt )
         p_sys->i_sdp_version++;
 
         fprintf( stderr, "sdp=%s", p_sys->psz_sdp );
+
+        /* Update the SAP announce */
+        if( p_sys->b_export_sap )
+        {
+            SapSetup( p_stream );
+        }
     }
 
     return id;
@@ -784,6 +825,12 @@ static int Del( sout_stream_t *p_stream, sout_stream_id_t *id )
     vlc_mutex_destroy( &id->lock_rtsp );
     if( id->rtsp_access ) free( id->rtsp_access );
 
+    /* Update the SAP announce */
+    if( p_sys->b_export_sap )
+    {
+        SapSetup( p_stream );
+    }
+
     free( id );
     return VLC_SUCCESS;
 }
@@ -899,6 +946,37 @@ static int AccessOutGrabberWrite( sout_access_out_t *p_access,
     return VLC_SUCCESS;
 }
 
+/****************************************************************************
+ * SAP:
+ ****************************************************************************/
+static int SapSetup( sout_stream_t *p_stream )
+{
+    sout_stream_sys_t *p_sys = p_stream->p_sys;
+    sout_instance_t   *p_sout = p_stream->p_sout;
+    announce_method_t *p_method = (announce_method_t *)
+                                  malloc(sizeof(announce_method_t));
+
+    /* Remove the previous session */
+    if( p_sys->p_session != NULL)
+    {
+        sout_AnnounceUnRegister( p_sout, p_sys->p_session);
+        sout_AnnounceSessionDestroy( p_sys->p_session );
+        p_sys->p_session = NULL;
+    }
+    p_method->i_type = METHOD_TYPE_SAP;
+    p_method->psz_address = NULL; /* FIXME */
+    p_method->i_ip_version = 4; /* FIXME ! */
+
+    if( p_sys->i_es > 0 && p_sys->psz_sdp && *p_sys->psz_sdp )
+    {
+        p_sys->p_session = sout_AnnounceRegisterSDP( p_sout, p_sys->psz_sdp,
+                                                     p_method );
+    }
+
+    free( p_method );
+    return VLC_SUCCESS;
+}
+
 /****************************************************************************
  * HTTP:
  ****************************************************************************/
index 2ac53132e0a6b4954010a35e419ebcd3334a33e9..6034fca4607389107e3838e3cb53972b7839be33 100644 (file)
 
 #include <vlc/vlc.h>
 #include <vlc/sout.h>
+
 #include "announce.h"
+#include "network.h"
 
-#define DEFAULT_IPV6_SCOPE "8"
+#define DEFAULT_IPV6_SCOPE '8'
+#define DEFAULT_PORT 1234
 
 /*****************************************************************************
  * Exported prototypes
@@ -59,7 +62,7 @@ struct sout_stream_sys_t
 {
     sout_mux_t           *p_mux;
     slp_session_t        *p_slp;
-    sap_session_t        *p_sap;
+    session_descriptor_t *p_session;
 };
 
 /*****************************************************************************
@@ -70,15 +73,14 @@ 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;
     slp_session_t       *p_slp = NULL;
-    sap_session_t       *p_sap = NULL;
+    session_descriptor_t *p_session = NULL;
 
     char *psz_mux      = sout_cfg_find_value( p_stream->p_cfg, "mux" );
     char *psz_access   = sout_cfg_find_value( p_stream->p_cfg, "access" );
     char *psz_url      = sout_cfg_find_value( p_stream->p_cfg, "url" );
-    char *psz_ipv      = sout_cfg_find_value( p_stream->p_cfg, "sap_ipv" );
-    char *psz_v6_scope = sout_cfg_find_value( p_stream->p_cfg, "sap_v6scope" );
     char *psz_sdp      = NULL;
 
+    vlc_url_t      *p_url;
     sout_cfg_t *p_sap_cfg = sout_cfg_find( p_stream->p_cfg, "sap" );
 #ifdef HAVE_SLP_H
     sout_cfg_t *p_slp_cfg = sout_cfg_find( p_stream->p_cfg, "slp" );
@@ -89,6 +91,9 @@ static int Open( vlc_object_t *p_this )
 
     char                *psz_mux_byext = NULL;
 
+    p_stream->p_sys        = malloc( sizeof( sout_stream_sys_t) );
+    p_stream->p_sys->p_session = NULL;
+
     msg_Dbg( p_this, "creating `%s/%s://%s'",
              psz_access, psz_mux, psz_url );
 
@@ -240,29 +245,92 @@ static int Open( vlc_object_t *p_this )
     msg_Dbg( p_stream, "mux opened" );
 
     /*  *** Create the SAP Session structure *** */
-    if( psz_access &&
-        p_sap_cfg &&
-        ( strstr( psz_access, "udp" ) || strstr( psz_access ,  "rtp" ) ) )
+    if( psz_access &&  p_sap_cfg && ( strstr( psz_access, "udp" ) ||
+                    strstr( psz_access ,  "rtp" ) ) )
     {
-        msg_Info( p_this, "SAP Enabled");
+        session_descriptor_t *p_session=  sout_AnnounceSessionCreate();
+        announce_method_t *p_method = sout_AnnounceMethodCreate(
+                                                  METHOD_TYPE_SAP);
 
-        if( psz_ipv == NULL )
+        /* Parse user input */
+        if( p_sap_cfg->psz_value )
+        {
+            char *psz_sap = p_sap_cfg->psz_value;
+            /* subconfig */
+            if( ! strncmp(psz_sap, "sap{", 4 ) )
+            {
+                sout_cfg_t *p_cfg;
+                char *psz_curr,*psz_null;
+                sout_cfg_parser( &psz_null, &p_cfg, psz_sap );
+                psz_curr =  sout_cfg_find_value( p_cfg,"name");
+                if( psz_curr != NULL)
+                {
+                    p_session->psz_name = strdup( psz_curr );
+                }
+                else
+                {
+                    p_session->psz_name = strdup( psz_url );
+
+                }
+
+                psz_curr = sout_cfg_find_value( p_cfg,"ip_version");
+                if( psz_curr != NULL)
+                {
+                    p_method->i_ip_version = atoi( psz_curr ) != 0 ?
+                                                     atoi(psz_curr) :
+                                                     4;
+                }
+            }
+            else
+            {
+                p_session->psz_name = strdup( p_sap_cfg->psz_value );
+            }
+        }
+        else
         {
-            psz_ipv = "4";
+            p_session->psz_name = strdup( psz_url );
         }
-        if( psz_v6_scope == NULL )
+
+        /* Now, parse the URL to extract host and port */
+        p_url = (vlc_url_t *)malloc( sizeof(vlc_url_t ) );
+        if ( ! p_url )
         {
-            psz_v6_scope = DEFAULT_IPV6_SCOPE;
+            return NULL;
         }
-        msg_Dbg( p_sout , "Creating SAP with IPv%i", atoi(psz_ipv) );
 
-        psz_sdp = SDPGenerateUDP(p_sap_cfg->psz_value ? p_sap_cfg->psz_value :
-                    psz_url, psz_url);
+        vlc_UrlParse( p_url, psz_url , 0);
 
-        p_sap = sout_SAPNew( p_sout , psz_sdp,atoi(psz_ipv), psz_v6_scope );
+        if (!p_url->psz_host)
+        {
+            return NULL;
+        }
 
-        if( !p_sap )
-            msg_Err( p_sout,"Unable to initialize SAP. SAP disabled");
+        if(p_url->i_port == 0)
+        {
+                p_url->i_port = DEFAULT_PORT;
+        }
+
+        p_session->psz_uri = p_url->psz_host;
+        p_session->i_port = p_url->i_port;
+        p_session->psz_sdp = NULL;
+
+        p_session->i_ttl = config_GetInt( p_sout,"ttl" );
+        p_session->i_payload = 33;
+
+        msg_Info( p_this, "SAP Enabled");
+
+        sout_AnnounceRegister( p_sout, p_session, p_method );
+
+        /* FIXME: Free p_method */
+
+        p_stream->p_sys->p_session = p_session;
+
+        if( p_url )
+        {
+            vlc_UrlClean( p_url );
+            free( p_url );
+            p_url = NULL;
+        }
     }
 
     /* *** Register with slp *** */
@@ -281,8 +349,8 @@ static int Open( vlc_object_t *p_this )
             p_slp = (slp_session_t*)malloc(sizeof(slp_session_t));
             if(!p_slp)
             {
-                msg_Warn(p_sout,"Out of memory");
-                if( p_sap ) free( p_sap );
+                msg_Warn(p_sout,"out of memory");
+//                if( p_sap ) free( p_sap );
                 return -1;
             }
             p_slp->psz_url= strdup(psz_url);
@@ -296,10 +364,8 @@ static int Open( vlc_object_t *p_this )
     p_stream->pf_del    = Del;
     p_stream->pf_send   = Send;
 
-    p_stream->p_sys        = malloc( sizeof( sout_stream_sys_t) );
     p_stream->p_sys->p_mux = p_mux;
     p_stream->p_sys->p_slp = p_slp;
-    p_stream->p_sys->p_sap = p_sap;
 
     return VLC_SUCCESS;
 }
@@ -313,8 +379,10 @@ static void Close( vlc_object_t * p_this )
     sout_stream_sys_t *p_sys    = p_stream->p_sys;
     sout_access_out_t *p_access = p_sys->p_mux->p_access;
 
-    if( p_sys->p_sap )
-        sout_SAPDelete( (sout_instance_t *)p_this , p_sys->p_sap );
+    if( p_sys->p_session != NULL )
+    {
+        sout_AnnounceUnRegister( p_stream->p_sout, p_sys->p_session );
+    }
 
 #ifdef HAVE_SLP_H
     if( p_sys->p_slp )
@@ -374,8 +442,5 @@ static int Send( sout_stream_t *p_stream, sout_stream_id_t *id,
 
     sout_MuxSendBuffer( p_sys->p_mux, id->p_input, p_buffer );
 
-    if( p_sys->p_sap )
-       sout_SAPSend( p_sout , p_sys->p_sap );
-
     return VLC_SUCCESS;
 }
index ffb926c98e11ea77678aa8433d9504de2c7ebbf6..d488aa9bb81d393dd6602492f68a9f56df9571e3 100644 (file)
@@ -2,7 +2,7 @@
  * libvlc.c: main libvlc source
  *****************************************************************************
  * Copyright (C) 1998-2004 VideoLAN
- * $Id: libvlc.c,v 1.118 2004/03/03 20:39:52 gbazin Exp $
+ * $Id$
  *
  * Authors: Vincent Seguin <seguin@via.ecp.fr>
  *          Samuel Hocevar <sam@zoy.org>
@@ -76,6 +76,8 @@
 #include "vlc_video.h"
 #include "video_output.h"
 
+#include "stream_output.h"
+
 #include "libvlc.h"
 
 /*****************************************************************************
@@ -1023,10 +1025,11 @@ int VLC_Play( int i_object )
  *****************************************************************************/
 int VLC_Stop( int i_object )
 {
-    intf_thread_t *   p_intf;
-    playlist_t    *   p_playlist;
-    vout_thread_t *   p_vout;
-    aout_instance_t * p_aout;
+    intf_thread_t      * p_intf;
+    playlist_t         * p_playlist;
+    vout_thread_t      * p_vout;
+    aout_instance_t    * p_aout;
+    announce_handler_t * p_announce;
     vlc_t *p_vlc = vlc_current_object( i_object );
 
     /* Check that the handle is valid */
@@ -1083,6 +1086,18 @@ int VLC_Stop( int i_object )
         aout_Delete( p_aout );
     }
 
+    /*
+     * Free announce handler(s?)
+     */
+    msg_Dbg( p_vlc, "removing announce handler" );
+    while( (p_announce = vlc_object_find( p_vlc, VLC_OBJECT_ANNOUNCE,
+                                                 FIND_CHILD ) ) )
+   {
+        vlc_object_detach( p_announce );
+        vlc_object_release( p_announce );
+        announce_HandlerDestroy( p_announce );
+   }
+
     if( i_object ) vlc_object_release( p_vlc );
     return VLC_SUCCESS;
 }
index 10089d1b1e98e0882c4c6db3a8db503c38867cba..5bc11150f4dad76390e7a0cd9b9239e101f8968d 100644 (file)
@@ -483,6 +483,19 @@ static char *ppsz_align_descriptions[] =
 #define ACCESS_OUTPUT_LONGTEXT N_( \
     "This is a legacy entry to let you configure access output modules")
 
+#define ANN_CAT_LONGTEXT N_( \
+    "These options allow you to set options for the session announcement" \
+    "subsystem." )
+
+#define ANN_SAPCTRL_TEXT N_("Control SAP flow")
+#define ANN_SAPCTRL_LONGTEXT N_("If this option is enabled, the flow on " \
+    "the SAP multicast address will be controlled. This is needed if you " \
+    "want to make announcements on the MBone" )
+
+#define ANN_SAPINTV_TEXT N_("SAP announcement interval")
+#define ANN_SAPINTV_LONGTEXT N_("When the SAP flow control is disabled, " \
+    "this lets you set the fixed interval between SAP announcements" )
+
 #define CPU_CAT_LONGTEXT N_( \
     "These options allow you to enable special CPU optimizations.\n" \
     "You should always leave all these enabled." )
@@ -895,6 +908,12 @@ vlc_module_begin();
                 ACCESS_OUTPUT_TEXT, ACCESS_OUTPUT_LONGTEXT, VLC_TRUE );
     add_integer( "ttl", 1, NULL, TTL_TEXT, TTL_LONGTEXT, VLC_TRUE );
 
+    /* Announce options */
+    add_category_hint( N_("Announce system"), ANN_CAT_LONGTEXT, VLC_TRUE );
+    add_bool( "sap-flow-control", VLC_FALSE, NULL, ANN_SAPCTRL_TEXT,
+                               ANN_SAPCTRL_LONGTEXT, VLC_TRUE );
+    add_integer( "sap-interval", 5, NULL, ANN_SAPINTV_TEXT,
+                               ANN_SAPINTV_LONGTEXT, VLC_TRUE );
 
     /* CPU options */
     add_category_hint( N_("CPU"), CPU_CAT_LONGTEXT, VLC_TRUE );
index 02303747351d1f221fc2c58feef44548a5836a29..361fc80e71cd807a3f3518f102ea6e65e2ae9e88 100644 (file)
@@ -265,6 +265,70 @@ int __net_Read( vlc_object_t *p_this, int fd, uint8_t *p_data, int i_data,
     return i_total;
 }
 
+/*****************************************************************************
+ * __net_ReadNonBlock:
+ *****************************************************************************
+ * Read from a network socket, non blocking mode (with timeout)
+ *****************************************************************************/
+int __net_ReadNonBlock( vlc_object_t *p_this, int fd, uint8_t *p_data,
+                        int i_data, mtime_t i_wait)
+{
+    struct timeval  timeout;
+    fd_set          fds_r, fds_e;
+    int             i_recv;
+    int             i_ret;
+
+    /* Initialize file descriptor set */
+    FD_ZERO( &fds_r );
+    FD_SET( fd, &fds_r );
+    FD_ZERO( &fds_e );
+    FD_SET( fd, &fds_e );
+
+    timeout.tv_sec = 0;
+    timeout.tv_usec = i_wait;
+
+    i_ret = select(fd + 1, &fds_r, NULL, &fds_e, &timeout);
+
+    if( i_ret < 0 && errno == EINTR )
+    {
+        return 0;
+    }
+    else if( i_ret < 0 )
+    {
+        msg_Err( p_this, "network select error (%s)", strerror(errno) );
+        return -1;
+    }
+    else if( i_ret == 0)
+    {
+        return 0;
+    }
+    else
+    {
+        if( ( i_recv = recv( fd, p_data, i_data, 0 ) ) < 0 )
+        {
+#ifdef WIN32
+            /* For udp only */
+            /* On win32 recv() will fail if the datagram doesn't fit inside
+             * the passed buffer, even though the buffer will be filled with
+             * the first part of the datagram. */
+            if( WSAGetLastError() == WSAEMSGSIZE )
+            {
+                msg_Err( p_this, "recv() failed. "
+                         "Increase the mtu size (--mtu option)" );
+            }
+            else
+                msg_Err( p_this, "recv failed (%i)", WSAGetLastError() );
+#else
+            msg_Err( p_this, "recv failed (%s)", strerror(errno) );
+#endif
+            return -1;
+        }
+        return i_recv;
+    }
+    /* We will never be here */
+    return -1;
+}
+
 /* Write exact amount requested */
 int __net_Write( vlc_object_t *p_this, int fd, uint8_t *p_data, int i_data )
 {
index 4f521424f1ee4362ca7f1a58a3fc3a5fa3d05569..0f42de7ae945589be4907316cc0e1d998ffb5b67 100644 (file)
@@ -160,6 +160,10 @@ void * __vlc_object_create( vlc_object_t *p_this, int i_type )
             i_size = sizeof( vlm_t );
             psz_type = "vlm dameon";
             break;
+        case VLC_OBJECT_ANNOUNCE:
+            i_size = sizeof( announce_handler_t );
+            psz_type = "announce handler";
+            break;
         default:
             i_size = i_type > 0
                       ? i_type > (int)sizeof(vlc_object_t)
diff --git a/src/stream_output/announce.c b/src/stream_output/announce.c
new file mode 100644 (file)
index 0000000..a89d44c
--- /dev/null
@@ -0,0 +1,322 @@
+/*****************************************************************************
+ * announce.c : announce handler
+ *****************************************************************************
+ * Copyright (C) 2002-2004 VideoLAN
+ * $Id: stream_output.c 7307 2004-04-07 23:13:03Z zorglub $
+ *
+ * Authors: Clément Stenac <zorglub@videolan.org>
+ *
+ * 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., 59 Temple Place - Suite 330, Boston, MA  02111, USA.
+ *****************************************************************************/
+
+/*****************************************************************************
+ * Preamble
+ *****************************************************************************/
+#include <stdlib.h>                                                /* free() */
+#include <stdio.h>                                              /* sprintf() */
+#include <string.h>                                            /* strerror() */
+
+#include <vlc/vlc.h>
+#include <vlc/sout.h>
+
+/*****************************************************************************
+ * Local prototypes
+ *****************************************************************************/
+#define FREE( p ) if( p ) { free( p ); (p) = NULL; }
+
+
+/****************************************************************************
+ * Sout-side functions
+ ****************************************************************************/
+
+/**
+ *  Register a new session with the announce handler
+ *
+ * \param p_sout a sout instance structure
+ * \param p_session a session descriptor
+ * \param p_method an announce method descriptor
+ * \return VLC_SUCCESS or an error
+ */
+int sout_AnnounceRegister( sout_instance_t *p_sout,
+                       session_descriptor_t *p_session,
+                       announce_method_t *p_method )
+{
+    int i_ret;
+    announce_handler_t *p_announce = (announce_handler_t*)
+                              vlc_object_find( p_sout,
+                                              VLC_OBJECT_ANNOUNCE,
+                                              FIND_ANYWHERE );
+
+    if( !p_announce )
+    {
+        msg_Dbg( p_sout, "No announce handler found, creating one" );
+        p_announce = announce_HandlerCreate( p_sout );
+        if( !p_announce )
+        {
+            msg_Err( p_sout, "Creation failed" );
+            return VLC_ENOMEM;
+        }
+        vlc_object_yield( p_announce );
+        msg_Dbg( p_sout,"Creation done" );
+    }
+
+    i_ret = announce_Register( p_announce, p_session, p_method );
+    vlc_object_release( p_announce );
+
+    return i_ret;
+}
+
+/**
+ *  Register a new session with the announce handler, using a pregenerated SDP
+ *
+ * \param p_sout a sout instance structure
+ * \param psz_sdp the SDP to register
+ * \param p_method an announce method descriptor
+ * \return the new session descriptor structure
+ */
+session_descriptor_t *sout_AnnounceRegisterSDP( sout_instance_t *p_sout,
+                          char *psz_sdp, announce_method_t *p_method )
+{
+    session_descriptor_t *p_session;
+    announce_handler_t *p_announce = (announce_handler_t*)
+                                     vlc_object_find( p_sout,
+                                              VLC_OBJECT_ANNOUNCE,
+                                              FIND_ANYWHERE );
+    if( !p_announce )
+    {
+        msg_Dbg( p_sout, "no announce handler found, creating one" );
+        p_announce = announce_HandlerCreate( p_sout );
+        if( !p_announce )
+        {
+            msg_Err( p_sout, "Creation failed" );
+            return NULL;
+        }
+        vlc_object_yield( p_announce );
+    }
+
+    if( p_method->i_type != METHOD_TYPE_SAP )
+    {
+        msg_Warn( p_sout,"forcing SAP announcement");
+    }
+
+    p_session = sout_AnnounceSessionCreate();
+    p_session->psz_sdp = strdup( psz_sdp );
+    announce_Register( p_announce, p_session, p_method );
+
+    vlc_object_release( p_announce );
+    return p_session;
+}
+
+/**
+ *  UnRegister an existing session
+ *
+ * \param p_sout a sout instance structure
+ * \param p_session the session descriptor
+ * \return VLC_SUCCESS or an error
+ */
+int sout_AnnounceUnRegister( sout_instance_t *p_sout,
+                             session_descriptor_t *p_session )
+{
+    int i_ret;
+    announce_handler_t *p_announce = (announce_handler_t*)
+                              vlc_object_find( p_sout,
+                                              VLC_OBJECT_ANNOUNCE,
+                                              FIND_ANYWHERE );
+    if( !p_announce )
+    {
+        msg_Dbg( p_sout, "Unable to remove announce: no announce handler" );
+        return VLC_ENOOBJ;
+    }
+    i_ret  = announce_UnRegister( p_announce, p_session );
+
+    vlc_object_release( p_announce );
+
+    return i_ret;
+}
+
+/**
+ * Create and initialize a session descriptor
+ *
+ * \return a new session descriptor
+ */
+session_descriptor_t * sout_AnnounceSessionCreate()
+{
+    session_descriptor_t *p_session;
+
+    p_session = (session_descriptor_t *)malloc( sizeof(session_descriptor_t));
+
+    if( p_session)
+    {
+        p_session->p_sap = NULL;
+        p_session->psz_sdp = NULL;
+        p_session->psz_name = NULL;
+        p_session->psz_uri = NULL;
+        p_session->i_port = 0;
+    }
+
+    return p_session;
+}
+
+/**
+ * Destroy a session descriptor and free all
+ *
+ * \param p_session the session to destroy
+ * \return Nothing
+ */
+void sout_AnnounceSessionDestroy( session_descriptor_t *p_session )
+{
+    if( p_session )
+    {
+        FREE( p_session->psz_name );
+        FREE( p_session->psz_uri );
+        FREE( p_session->psz_sdp );
+        FREE( p_session );
+    }
+}
+
+/**
+ * Create and initialize an announcement method structure
+ *
+ * \param i_type METHOD_TYPE_SAP or METHOD_TYPE_SLP
+ * \return a new announce_method structure
+ */
+announce_method_t * sout_AnnounceMethodCreate( int i_type )
+{
+    announce_method_t *p_method;
+
+    p_method = (announce_method_t *)malloc( sizeof(announce_method_t) );
+
+    if( p_method )
+    {
+        p_method->i_type = i_type;
+        if( i_type == METHOD_TYPE_SAP )
+        {
+            /* Default values */
+            p_method->psz_address = NULL;
+            p_method->i_ip_version = 4 ;
+            p_method->psz_ipv6_scope = strdup("8");
+        }
+    }
+    return p_method;
+}
+
+/************************************************************************
+ * Announce handler functions (private)
+ ************************************************************************/
+
+/**
+ * Create the announce handler object
+ *
+ * \param p_this a vlc_object structure
+ * \return the new announce handler or NULL on error
+ */
+announce_handler_t *__announce_HandlerCreate( vlc_object_t *p_this )
+{
+    announce_handler_t *p_announce;
+
+    p_announce = vlc_object_create( p_this, VLC_OBJECT_ANNOUNCE );
+
+    if( !p_announce )
+    {
+        msg_Err( p_this, "out of memory" );
+        return NULL;
+    }
+
+    p_announce->p_sap = NULL;
+
+    vlc_object_attach( p_announce, p_this->p_vlc);
+
+
+    return p_announce;
+}
+
+/**
+ * Destroy a  announce handler object
+ *
+ * \param p_announce the announce handler to destroy
+ * \return VLC_SUCCESS or an error
+ */
+int announce_HandlerDestroy( announce_handler_t *p_announce )
+{
+
+    if( p_announce->p_sap )
+    {
+        p_announce->p_sap->b_die = VLC_TRUE;
+        /* Wait for the SAP thread to exit */
+        vlc_thread_join( p_announce->p_sap );
+        announce_SAPHandlerDestroy( p_announce->p_sap );
+    }
+
+    /* Free the structure */
+    vlc_object_destroy( p_announce );
+
+    return VLC_SUCCESS;
+}
+
+/* Register an announce */
+int announce_Register( announce_handler_t *p_announce,
+                       session_descriptor_t *p_session,
+                       announce_method_t *p_method )
+{
+
+    msg_Dbg( p_announce, "registering announce");
+    if( p_method->i_type == METHOD_TYPE_SAP )
+    {
+        /* Do we already have a SAP announce handler ? */
+        if( !p_announce->p_sap )
+        {
+            msg_Dbg( p_announce, "creating SAP announce handler");
+            sap_handler_t *p_sap = announce_SAPHandlerCreate( p_announce );
+            if( !p_sap )
+            {
+                msg_Err( p_announce, "SAP handler creation failed" );
+                return VLC_ENOOBJ;
+            }
+            p_announce->p_sap = p_sap;
+        }
+        /* this will set p_session->p_sap for later deletion */
+        msg_Dbg( p_announce, "adding SAP session");
+        p_announce->p_sap->pf_add( p_announce->p_sap, p_session, p_method );
+    }
+    else if( p_method->i_type == METHOD_TYPE_SLP )
+    {
+        msg_Dbg( p_announce, "SLP unsupported at the moment" );
+        return VLC_EGENERIC;
+    }
+    else
+    {
+        msg_Dbg( p_announce, "Announce type unsupported" );
+        return VLC_EGENERIC;
+    }
+    return VLC_SUCCESS;;
+}
+
+
+/* Unregister an announce */
+int announce_UnRegister( announce_handler_t *p_announce,
+                  session_descriptor_t *p_session )
+{
+    msg_Dbg( p_announce, "unregistering announce" );
+    if( p_session->p_sap != NULL ) /* SAP Announce */
+    {
+        if( !p_announce->p_sap )
+        {
+            msg_Err( p_announce, "can't remove announce, no SAP handler");
+            return VLC_ENOOBJ;
+        }
+        p_announce->p_sap->pf_del( p_announce->p_sap, p_session );
+    }
+    return VLC_SUCCESS;
+}
diff --git a/src/stream_output/sap.c b/src/stream_output/sap.c
new file mode 100644 (file)
index 0000000..a171a85
--- /dev/null
@@ -0,0 +1,526 @@
+/*****************************************************************************
+ * sap.c : SAP announce handler
+ *****************************************************************************
+ * Copyright (C) 2002-2004 VideoLAN
+ * $Id: sap.c 7307 2004-04-07 23:13:03Z fenrir $
+ *
+ * Authors: Clément Stenac <zorglub@videolan.org>
+ *
+ * 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., 59 Temple Place - Suite 330, Boston, MA  02111, USA.
+ *****************************************************************************/
+
+/*****************************************************************************
+ * Preamble
+ *****************************************************************************/
+#include <stdlib.h>                                                /* free() */
+#include <stdio.h>                                              /* sprintf() */
+#include <string.h>                                            /* strerror() */
+
+#include <vlc/vlc.h>
+#include <vlc/sout.h>
+
+#include <network.h>
+
+#define SAP_IPV4_ADDR "224.2.127.254" /* Standard port and address for SAP */
+#define SAP_PORT 9875
+
+#define SAP_IPV6_ADDR_1 "FF0"
+#define SAP_IPV6_ADDR_2 "::2:7FFE"
+
+#define DEFAULT_IPV6_SCOPE '8'
+
+#define DEFAULT_PORT "1234"
+
+#undef EXTRA_DEBUG
+
+/*****************************************************************************
+ * Local prototypes
+ *****************************************************************************/
+static void RunThread( vlc_object_t *p_this);
+static int CalculateRate( sap_handler_t *p_sap, sap_address_t *p_address );
+static int SDPGenerate( sap_handler_t *p_sap, session_descriptor_t *p_session );
+
+static int announce_SendSAPAnnounce( sap_handler_t *p_sap,
+                                     sap_session_t *p_session );
+
+
+static int announce_SAPAnnounceAdd( sap_handler_t *p_sap,
+                             session_descriptor_t *p_session,
+                             announce_method_t *p_method );
+
+static int announce_SAPAnnounceDel( sap_handler_t *p_sap,
+                             session_descriptor_t *p_session );
+
+#define FREE( p ) if( p ) { free( p ); (p) = NULL; }
+
+
+/**
+ * Create the SAP handler
+ *
+ * \param p_announce the parent announce_handler
+ * \return the newly created SAP handler or NULL on error
+ */
+sap_handler_t *announce_SAPHandlerCreate( announce_handler_t *p_announce )
+{
+    sap_handler_t *p_sap;
+
+    p_sap = vlc_object_create( p_announce, sizeof( sap_handler_t ) );
+
+    if( !p_sap )
+    {
+        msg_Err( p_announce, "out of memory" );
+        return NULL;
+    }
+
+    vlc_mutex_init( p_sap, &p_sap->object_lock );
+
+    p_sap->pf_add = announce_SAPAnnounceAdd;
+    p_sap->pf_del = announce_SAPAnnounceDel;
+
+    p_sap->i_sessions = 0;
+    p_sap->i_addresses = 0;
+    p_sap->i_current_session = 0;
+
+    p_sap->b_control = config_GetInt( p_sap, "sap-flow-control");
+
+    if( vlc_thread_create( p_sap, "sap handler", RunThread,
+                       VLC_THREAD_PRIORITY_LOW, VLC_FALSE ) )
+    {
+        msg_Dbg( p_announce, "Unable to spawn SAP handler thread");
+        free( p_sap );
+        return NULL;
+    };
+    msg_Dbg( p_announce, "thread created, %i sessions", p_sap->i_sessions);
+    return p_sap;
+}
+
+/**
+ *  Destroy the SAP handler
+ *  \param p_this the SAP Handler to destroy
+ *  \return nothing
+ */
+void announce_SAPHandlerDestroy( sap_handler_t *p_sap )
+{
+    int i;
+
+    vlc_mutex_destroy( &p_sap->object_lock );
+
+    /* Free the remaining sessions */
+    for( i = 0 ; i< p_sap->i_sessions ; i++)
+    {
+        sap_session_t *p_session = p_sap->pp_sessions[i];
+        FREE( p_session->psz_sdp );
+        FREE( p_session->psz_data );
+        REMOVE_ELEM( p_sap->pp_sessions, p_sap->i_sessions , i );
+        FREE( p_session );
+    }
+
+    /* Free the remaining addresses */
+    for( i = 0 ; i< p_sap->i_addresses ; i++)
+    {
+        sap_address_t *p_address = p_sap->pp_addresses[i];
+        FREE( p_address->psz_address );
+        if( p_address->i_rfd > -1 )
+        {
+            net_Close( p_address->i_rfd );
+        }
+        if( p_address->i_wfd > -1 && p_sap->b_control )
+        {
+            net_Close( p_address->i_wfd );
+        }
+        REMOVE_ELEM( p_sap->pp_addresses, p_sap->i_addresses, i );
+        FREE( p_address );
+    }
+
+    /* Free the structure */
+    vlc_object_destroy( p_sap );
+}
+
+/**
+ * main SAP handler thread
+ * \param p_this the SAP Handler object
+ * \return nothing
+ */
+static void RunThread( vlc_object_t *p_this)
+{
+    sap_handler_t *p_sap = (sap_handler_t*)p_this;
+    sap_session_t *p_session;
+
+    while( !p_sap->b_die )
+    {
+        int i;
+
+        /* If needed, get the rate info */
+        if( p_sap->b_control == VLC_TRUE )
+        {
+            for( i = 0 ; i< p_sap->i_addresses ; i++)
+            {
+                if( p_sap->pp_addresses[i]->b_enabled == VLC_TRUE )
+                {
+                    CalculateRate( p_sap, p_sap->pp_addresses[i] );
+                }
+            }
+        }
+
+        /* Find the session to announce */
+        vlc_mutex_lock( &p_sap->object_lock );
+        if( p_sap->i_sessions > p_sap->i_current_session + 1)
+        {
+            p_sap->i_current_session++;
+        }
+        else if( p_sap->i_sessions > 0)
+        {
+            p_sap->i_current_session = 0;
+        }
+        else
+        {
+            vlc_mutex_unlock( &p_sap->object_lock );
+            msleep( SAP_IDLE );
+            continue;
+        }
+        p_session = p_sap->pp_sessions[p_sap->i_current_session];
+        vlc_mutex_unlock( &p_sap->object_lock );
+
+        /* And announce it */
+        if( p_session->p_address->b_enabled == VLC_TRUE &&
+            p_session->p_address->b_ready == VLC_TRUE )
+        {
+            announce_SendSAPAnnounce( p_sap, p_session );
+        }
+
+        msleep( SAP_IDLE );
+    }
+}
+
+/* Add a SAP announce */
+static int announce_SAPAnnounceAdd( sap_handler_t *p_sap,
+                             session_descriptor_t *p_session,
+                             announce_method_t *p_method )
+{
+    int i;
+    char *psz_type = "application/sdp";
+    int i_header_size;
+    char *psz_head;
+    vlc_bool_t b_found = VLC_FALSE;
+    sap_session_t *p_sap_session;
+
+    vlc_mutex_lock( &p_sap->object_lock );
+
+    /* If needed, build the SDP */
+    if( !p_session->psz_sdp )
+    {
+        if ( SDPGenerate( p_sap, p_session ) != VLC_SUCCESS )
+        {
+            vlc_mutex_unlock( &p_sap->object_lock );
+            return VLC_EGENERIC;
+        }
+    }
+
+    if( !p_method->psz_address )
+    {
+        if( p_method->i_ip_version == 6 )
+        {
+            char sz_scope;
+            if( p_method->psz_ipv6_scope != NULL )
+            {
+                sz_scope = *p_method->psz_ipv6_scope;
+            }
+            else
+            {
+                sz_scope = DEFAULT_IPV6_SCOPE;
+            }
+            p_method->psz_address = (char*)malloc( 30*sizeof(char ));
+            sprintf( p_method->psz_address, "%s%c%s",
+                            SAP_IPV6_ADDR_1, sz_scope, SAP_IPV6_ADDR_2 );
+        }
+        else
+        {
+            /* IPv4 */
+            p_method->psz_address = (char*)malloc( 15*sizeof(char) );
+            sprintf(p_method->psz_address, SAP_IPV4_ADDR );
+        }
+    }
+    msg_Dbg( p_sap, "using SAP address: %s",p_method->psz_address);
+
+    /* XXX: Check for dupes */
+    p_sap_session = (sap_session_t*)malloc(sizeof(sap_session_t));
+
+    p_sap_session->psz_sdp = strdup( p_session->psz_sdp );
+    p_sap_session->i_last = 0;
+
+    /* Add the address to the buffer */
+    for( i = 0; i< p_sap->i_addresses; i++)
+    {
+        if( !strcmp( p_method->psz_address,
+             p_sap->pp_addresses[i]->psz_address ) )
+        {
+            p_sap_session->p_address = p_sap->pp_addresses[i];
+            b_found = VLC_TRUE;
+            break;
+        }
+    }
+    if( b_found == VLC_FALSE )
+    {
+        sap_address_t *p_address = (sap_address_t *)
+                                    malloc( sizeof(sap_address_t) );
+        if( !p_address )
+        {
+            msg_Err( p_sap, "out of memory" );
+            return VLC_ENOMEM;
+        }
+        p_address->psz_address = strdup( p_method->psz_address );
+        p_address->i_port  =  9875;
+        p_address->i_wfd = net_OpenUDP( p_sap, "", 0,
+                                        p_address->psz_address,
+                                        p_address->i_port );
+
+        if( p_sap->b_control == VLC_TRUE )
+        {
+            p_address->i_rfd = net_OpenUDP( p_sap, p_method->psz_address,
+                                            p_address->i_port,
+                                            "", 0 );
+            p_address->i_buff = 0;
+            p_address->b_enabled = VLC_TRUE;
+            p_address->b_ready = VLC_FALSE;
+            p_address->i_limit = 10000; /* 10000 bps */
+            p_address->t1 = 0;
+        }
+        else
+        {
+            p_address->b_enabled = VLC_TRUE;
+            p_address->b_ready = VLC_TRUE;
+            p_address->i_interval = config_GetInt( p_sap,"sap-interval");
+        }
+
+        if( p_address->i_wfd == -1 || (p_address->i_rfd == -1
+                                        && p_sap->b_control ) )
+        {
+            msg_Warn( p_sap, "disabling address" );
+            p_address->b_enabled = VLC_FALSE;
+        }
+
+        INSERT_ELEM( p_sap->pp_addresses,
+                     p_sap->i_addresses,
+                     p_sap->i_addresses,
+                     p_address );
+        p_sap_session->p_address = p_address;
+    }
+
+    /* Build the SAP Headers */
+    i_header_size = 8 + strlen( psz_type ) + 1;
+    psz_head = (char *) malloc( i_header_size * sizeof( char ) );
+    if( ! psz_head )
+    {
+        msg_Err( p_sap, "out of memory" );
+        return VLC_ENOMEM;
+    }
+
+    psz_head[0] = 0x20; /* Means IPv4, not encrypted, not compressed */
+    psz_head[1] = 0x00; /* No authentification */
+    psz_head[2] = 0x42; /* Msg id hash */
+    psz_head[3] = 0x12; /* Msg id hash 2 */
+
+    psz_head[4] = 0x01; /* Source IP  FIXME: we should get the real address */
+    psz_head[5] = 0x02; /* idem */
+    psz_head[6] = 0x03; /* idem */
+    psz_head[7] = 0x04; /* idem */
+
+    strncpy( psz_head + 8, psz_type, 15 );
+    psz_head[ i_header_size-1 ] = '\0';
+    p_sap_session->i_length = i_header_size + strlen( p_sap_session->psz_sdp);
+
+    p_sap_session->psz_data = (char *)malloc( sizeof(char)*
+                                              p_sap_session->i_length );
+
+    /* Build the final message */
+    memcpy( p_sap_session->psz_data, psz_head, i_header_size );
+    memcpy( p_sap_session->psz_data+i_header_size, p_sap_session->psz_sdp,
+            strlen( p_sap_session->psz_sdp) );
+
+    /* Enqueue the announce */
+    INSERT_ELEM( p_sap->pp_sessions,
+                 p_sap->i_sessions,
+                 p_sap->i_sessions,
+                 p_sap_session );
+    msg_Dbg( p_sap,"Addresses: %i  Sessions: %i",
+                   p_sap->i_addresses,p_sap->i_sessions);
+
+    /* Remember the SAP session for later deletion */
+    p_session->p_sap = p_sap_session;
+
+    vlc_mutex_unlock( &p_sap->object_lock );
+
+    return VLC_SUCCESS;
+}
+
+/* Remove a SAP Announce */
+static int announce_SAPAnnounceDel( sap_handler_t *p_sap,
+                             session_descriptor_t *p_session )
+{
+    int i;
+    vlc_mutex_lock( &p_sap->object_lock );
+
+    msg_Dbg( p_sap,"removing SAP announce %p",p_session->p_sap);
+
+    /* Dequeue the announce */
+    for( i = 0; i< p_sap->i_sessions; i++)
+    {
+        if( p_session->p_sap == p_sap->pp_sessions[i] )
+        {
+            REMOVE_ELEM( p_sap->pp_sessions,
+                         p_sap->i_sessions,
+                         i );
+            break;
+        }
+    }
+
+    /* XXX: Dequeue the adress too if it is not used anymore
+     * TODO: address refcount */
+
+    msg_Dbg( p_sap,"%i announces remaining", p_sap->i_sessions );
+
+    vlc_mutex_unlock( &p_sap->object_lock );
+
+    return VLC_SUCCESS;
+}
+
+static int announce_SendSAPAnnounce( sap_handler_t *p_sap,
+                                     sap_session_t *p_session )
+{
+    int i_ret;
+
+    /* This announce has never been sent yet */
+    if( p_session->i_last == 0 )
+    {
+        p_session->i_next = mdate()+ p_session->p_address->i_interval*1000000;
+        p_session->i_last = 1;
+        return VLC_SUCCESS;
+    }
+
+    if( p_session->i_next < mdate() )
+    {
+#ifdef EXTRA_DEBUG
+        msg_Dbg( p_sap, "Sending announce");
+#endif
+        i_ret = net_Write( p_sap, p_session->p_address->i_wfd,
+                           p_session->psz_data,
+                           p_session->i_length );
+        if( i_ret  != p_session->i_length )
+        {
+            msg_Warn( p_sap, "SAP send failed on address %s (%i %i)",
+                   p_session->p_address->psz_address,
+                   i_ret, p_session->i_length );
+        }
+        p_session->i_last = p_session->i_next;
+        p_session->i_next = p_session->i_last
+                            + p_session->p_address->i_interval*1000000;
+    }
+    else
+    {
+        return VLC_SUCCESS;
+    }
+    return VLC_SUCCESS;
+}
+
+static int SDPGenerate( sap_handler_t *p_sap, session_descriptor_t *p_session )
+{
+    p_session->psz_sdp = (char *)malloc(
+                            sizeof("v=0\n"
+                                   "o=- 12 12 IN IP4 127.0.0.1\n" /* FIXME */
+                                   "s=\n"
+                                   "c=IN IP4 /\n"
+                                   "m=video  udp\n"
+                                   "a=tool:VLC "VERSION"\n"
+                                   "a=type:broadcast")
+                           + strlen( p_session->psz_name )
+                           + strlen( p_session->psz_uri ) + 300 );
+    if( !p_session->psz_sdp )
+    {
+        msg_Err( p_sap, "out of memory" );
+        return VLC_ENOMEM;
+    }
+    sprintf( p_session->psz_sdp,
+                            "v=0\n"
+                            "o=- 12 12 IN IP4 127.0.0.1\n"
+                            "s=%s\n"
+                            "c=IN IP4 %s/%d\n"
+                            "m=video %d udp %d\n"
+                            "a=tool:VLC "VERSION"\n"
+                            "a=type:broadcast",
+                            p_session->psz_name,
+                            p_session->psz_uri, p_session->i_ttl,
+                            p_session->i_port, p_session->i_payload );
+    msg_Dbg( p_sap, "Generated SDP (%i bytes):\n%s", strlen(p_session->psz_sdp),
+                    p_session->psz_sdp );
+    return VLC_SUCCESS;
+}
+
+static int CalculateRate( sap_handler_t *p_sap, sap_address_t *p_address )
+{
+    int i_read;
+    char buffer[SAP_MAX_BUFFER];
+    int i_tot = 0;
+    mtime_t i_temp;
+    int i_rate;
+
+    if( p_address->t1 == 0 )
+    {
+        p_address->t1 = mdate();
+        return VLC_SUCCESS;
+    }
+    do
+    {
+        /* Might be too slow if we have huge data */
+        i_read = net_ReadNonBlock( p_sap, p_address->i_rfd, buffer,
+                                   SAP_MAX_BUFFER, 0 );
+        i_tot += i_read;
+    } while( i_read > 0 && i_tot < SAP_MAX_BUFFER );
+
+    i_temp = mdate();
+
+    /* We calculate the rate every 5 seconds */
+    if( i_temp - p_address->t1 < 5000000 )
+    {
+        p_address->i_buff += i_tot;
+        return VLC_SUCCESS;
+    }
+
+    /* Bits/second */
+    i_rate = (int)(8*1000000*((mtime_t)p_address->i_buff + (mtime_t)i_tot ) /
+                        (i_temp - p_address->t1 ));
+
+    p_address->i_limit = 10000;
+
+    p_address->i_interval = ((1000*i_rate / p_address->i_limit) *
+                            (MAX_INTERVAL - MIN_INTERVAL))/1000 + MIN_INTERVAL;
+
+    if( p_address->i_interval > MAX_INTERVAL || p_address->i_interval < 0 )
+    {
+        p_address->i_interval = MAX_INTERVAL;
+    }
+#ifdef EXTRA_DEBUG
+    msg_Dbg( p_sap,"%s:%i : Rate=%i, Interval = %i s",
+                    p_address->psz_address,p_address->i_port,
+                    i_rate,
+                    p_address->i_interval );
+#endif
+
+    p_address->b_ready = VLC_TRUE;
+
+    p_address->t1 = i_temp;
+    p_address->i_buff = 0;
+
+    return VLC_SUCCESS;
+}