[vlc-devel] [PATCH] Merge VoD module into RTP stream output module

Pierre Ynard linkfanel at yahoo.fr
Thu Jun 3 07:27:32 CEST 2010


Hello,

This patch is aimed at bringing a cleaner and better working solution
for VoD than the bad hack that we currently have. Instead of (badly)
duplicating large portions of codes, it reuses the existing RTSP stack
and factorizes the RTP payload code, and provides support for a number
of ES not limited to one audio and one video tracks, as well as for
missing features in RTSP headers.

The VoD module is responsible for starting and running the RTSP stack.
When a VoD media instance is started, the RTP stream output hooks back
into the VoD code (and through it, into the RTSP stack), to retrieve
accurate, already set up parameters, and provide playback data.

It currently does so by walking up the object tree from the RTP sout to
the VoD object. I used to use a simple vlc_object_find(INPUT, PARENT)
(which was pretty safe since the VoD core doesn't use sout-keep, if
I understand the situation correctly), but this was recently broken.
Once this is figured out, I can properly rewrite this part. Or, an
alternative approach would be to pass a pointer to the vod_media_t in
the #rtp stream output chain, but this is a little ugly too (though it
would save a lock). Opinions?


diff --git a/modules/stream_out/Modules.am b/modules/stream_out/Modules.am
index 1bfafeb..8c757ba 100644
--- a/modules/stream_out/Modules.am
+++ b/modules/stream_out/Modules.am
@@ -34,7 +34,7 @@ libvlc_LTLIBRARIES += \
 libvlc_LTLIBRARIES += \
 	libstream_out_rtp_plugin.la
 libstream_out_rtp_plugin_la_SOURCES = \
-	rtp.c rtp.h rtpfmt.c rtcp.c rtsp.c
+	rtp.c rtp.h rtpfmt.c rtcp.c rtsp.c vod.c
 libstream_out_rtp_plugin_la_CFLAGS = $(AM_CFLAGS)
 libstream_out_rtp_plugin_la_LIBADD = $(AM_LIBADD)
 libstream_out_rtp_plugin_la_DEPENDENCIES =
diff --git a/modules/stream_out/rtp.c b/modules/stream_out/rtp.c
index ef40d88..86e6025 100644
--- a/modules/stream_out/rtp.c
+++ b/modules/stream_out/rtp.c
@@ -1,10 +1,11 @@
 /*****************************************************************************
  * rtp.c: rtp stream output module
  *****************************************************************************
- * Copyright (C) 2003-2004 the VideoLAN team
+ * Copyright (C) 2003-2004, 2010 the VideoLAN team
  * Copyright © 2007-2008 Rémi Denis-Courmont
  *
  * Authors: Laurent Aimar <fenrir at via.ecp.fr>
+ *          Pierre Ynard
  *
  * 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
@@ -38,7 +39,6 @@
 #include <vlc_url.h>
 #include <vlc_network.h>
 #include <vlc_fs.h>
-#include <vlc_strings.h>
 #include <vlc_rand.h>
 #ifdef HAVE_SRTP
 # include <srtp.h>
@@ -160,6 +160,13 @@ static const char *const ppsz_protocols[] = {
 #define RFC3016_LONGTEXT N_( \
     "This allows you to stream MPEG4 LATM audio streams (see RFC3016)." )
 
+#define RTSP_HOST_TEXT N_( "RTSP host address" )
+#define RTSP_HOST_LONGTEXT N_( \
+    "This defines the address, port and path the RTSP VOD server will listen " \
+    "on.\nSyntax is address:port/path. The default is to listen on all "\
+    "interfaces (address 0.0.0.0), on port 554, with no path.\nTo listen " \
+    "only on the local interface, use \"localhost\" as address." )
+
 static int  Open ( vlc_object_t * );
 static void Close( vlc_object_t * );
 
@@ -221,7 +228,22 @@ vlc_module_begin ()
     add_bool( SOUT_CFG_PREFIX "mp4a-latm", false, NULL, RFC3016_TEXT,
                  RFC3016_LONGTEXT, false )
 
+    add_string( SOUT_CFG_PREFIX "vod-session", "", NULL, "vod",
+                 "vod", true )
+
     set_callbacks( Open, Close )
+
+    add_submodule ()
+    set_shortname( N_("RTSP VoD" ) )
+    set_description( N_("RTSP VoD server") )
+    set_category( CAT_SOUT )
+    set_subcategory( SUBCAT_SOUT_VOD )
+    set_capability( "vod server", 0 )
+    set_callbacks( OpenVoD, CloseVoD )
+    add_shortcut( "rtsp" )
+    add_string ( "rtsp-host", NULL, NULL, RTSP_HOST_TEXT,
+                 RTSP_HOST_LONGTEXT, true )
+
 vlc_module_end ()
 
 /*****************************************************************************
@@ -231,7 +253,7 @@ 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", "caching", "key", "salt",
-    "mp4a-latm", NULL
+    "mp4a-latm", "vod-session", NULL
 };
 
 static sout_stream_id_t *Add ( sout_stream_t *, es_format_t * );
@@ -290,6 +312,10 @@ struct sout_stream_sys_t
     int       i_ttl:9;
     bool      b_latm;
 
+    /* VoD */
+    char     *psz_vod_session;
+    vod_media_t *p_vod_media;
+
     /* in case we do TS/PS over rtp */
     sout_mux_t        *p_mux;
     sout_access_out_t *p_grab;
@@ -314,7 +340,6 @@ struct sout_stream_id_t
     sout_stream_t *p_stream;
     /* rtp field */
     uint16_t    i_sequence;
-    uint8_t     i_payload_type;
     bool        b_ts_init;
     uint32_t    i_ts_offset;
     uint8_t     ssrc[4];
@@ -323,13 +348,8 @@ struct sout_stream_id_t
     uint16_t    i_seq_sent_next;
 
     /* for sdp */
-    const char  *psz_enc;
-    char        *psz_fmtp;
-    int          i_clock_rate;
+    rtp_format_t rtp_fmt;
     int          i_port;
-    int          i_cat;
-    int          i_channels;
-    int          i_bitrate;
 
     /* Packetizer specific fields */
     int                 i_mtu;
@@ -445,11 +465,33 @@ static int Open( vlc_object_t *p_this )
     free (psz);
     var_Create (p_this, "dccp-service", VLC_VAR_STRING);
 
-    if( ( p_sys->psz_destination == NULL ) && !b_rtsp )
+    int64_t i_pts_init;
+    p_sys->psz_vod_session = var_GetNonEmptyString (p_stream, SOUT_CFG_PREFIX
+                                                              "vod-session");
+    if (p_sys->psz_vod_session != NULL)
     {
-        msg_Err( p_stream, "missing destination and not in RTSP mode" );
-        free( p_sys );
-        return VLC_EGENERIC;
+        const char *mux;
+        p_sys->p_vod_media = vod_get_session(p_stream, p_sys->psz_vod_session,
+                                             &mux, &i_pts_init);
+        if (p_sys->p_vod_media == NULL)
+        {
+            msg_Err(p_stream, "no vod session '%s'", p_sys->psz_vod_session);
+            free(p_sys->psz_vod_session);
+            free(p_sys);
+            return VLC_EGENERIC;
+        }
+        var_SetString(p_stream, SOUT_CFG_PREFIX "mux", mux);
+    }
+    else
+    {
+        p_sys->p_vod_media = NULL;
+
+        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" );
@@ -470,8 +512,8 @@ static int Open( vlc_object_t *p_this )
      * PTS for timestamp computations, and then actual PTS will catch
      * up using offsets. */
     p_sys->i_npt_zero = VLC_TS_INVALID;
-    p_sys->i_pts_zero = mdate(); /* arbitrary value, could probably be
-                                  * random */
+    p_sys->i_pts_zero = p_sys->p_vod_media != NULL ? i_pts_init : mdate();
+                        /* arbitrary value, could probably be random */
     p_sys->payload_bitmap = 0xFFFFFFFF;
     p_sys->i_es = 0;
     p_sys->es   = NULL;
@@ -505,6 +547,7 @@ static int Open( vlc_object_t *p_this )
             free( psz );
             vlc_mutex_destroy( &p_sys->lock_sdp );
             vlc_mutex_destroy( &p_sys->lock_es );
+            free( p_sys->psz_vod_session );
             free( p_sys->psz_destination );
             free( p_sys );
             return VLC_EGENERIC;
@@ -520,6 +563,7 @@ static int Open( vlc_object_t *p_this )
             sout_AccessOutDelete( p_sys->p_grab );
             vlc_mutex_destroy( &p_sys->lock_sdp );
             vlc_mutex_destroy( &p_sys->lock_es );
+            free( p_sys->psz_vod_session );
             free( p_sys->psz_destination );
             free( p_sys );
             return VLC_EGENERIC;
@@ -532,6 +576,7 @@ static int Open( vlc_object_t *p_this )
             sout_AccessOutDelete( p_sys->p_grab );
             vlc_mutex_destroy( &p_sys->lock_sdp );
             vlc_mutex_destroy( &p_sys->lock_es );
+            free( p_sys->psz_vod_session );
             free( p_sys->psz_destination );
             free( p_sys );
             return VLC_EGENERIC;
@@ -638,6 +683,7 @@ static void Close( vlc_object_t * p_this )
 #endif
         free( p_sys->psz_sdp_file );
     }
+    free( p_sys->psz_vod_session );
     free( p_sys->psz_destination );
     free( p_sys );
 }
@@ -673,7 +719,7 @@ static void SDPHandleUrl( sout_stream_t *p_stream, const char *psz_url )
         }
 
         /* FIXME test if destination is multicast or no destination at all */
-        p_sys->rtsp = RtspSetup( p_stream, &url );
+        p_sys->rtsp = RtspSetup( VLC_OBJECT(p_stream), NULL, &url );
         if( p_sys->rtsp == NULL )
             msg_Err( p_stream, "cannot export SDP as RTSP" );
         else
@@ -681,6 +727,7 @@ static void SDPHandleUrl( sout_stream_t *p_stream, const char *psz_url )
         {
             sout_stream_id_t *id = p_sys->es[0];
             id->rtsp_id = RtspAddId( p_sys->rtsp, id, GetDWBE( id->ssrc ),
+                                     id->rtp_fmt.clock_rate,
                                      p_sys->psz_destination, p_sys->i_ttl,
                                      id->i_port, id->i_port + 1 );
         }
@@ -809,9 +856,10 @@ char *SDPGenerate( sout_stream_t *p_stream, const char *rtsp_url )
     for( i = 0; i < p_sys->i_es; i++ )
     {
         sout_stream_id_t *id = p_sys->es[i];
+        rtp_format_t *rtp_fmt = &id->rtp_fmt;
         const char *mime_major; /* major MIME type */
 
-        switch( id->i_cat )
+        switch( rtp_fmt->cat )
         {
             case VIDEO_ES:
                 mime_major = "video";
@@ -827,9 +875,9 @@ char *SDPGenerate( sout_stream_t *p_stream, const char *rtsp_url )
         }
 
         sdp_AddMedia( &psz_sdp, mime_major, proto, inclport * id->i_port,
-                      id->i_payload_type, false, id->i_bitrate,
-                      id->psz_enc, id->i_clock_rate, id->i_channels,
-                      id->psz_fmtp);
+                      rtp_fmt->payload_type, false, rtp_fmt->bitrate,
+                      rtp_fmt->ptname, rtp_fmt->clock_rate, rtp_fmt->channels,
+                      rtp_fmt->fmtp);
 
         /* cf RFC4566 §5.14 */
         if( inclport && !p_sys->rtcp_mux && (id->i_port & 1) )
@@ -862,18 +910,6 @@ out:
  * RTP mux
  *****************************************************************************/
 
-static void sprintf_hexa( char *s, uint8_t *p_data, int i_data )
-{
-    static const char hex[16] = "0123456789abcdef";
-
-    for( int i = 0; i < i_data; i++ )
-    {
-        s[2*i+0] = hex[(p_data[i]>>4)&0xf];
-        s[2*i+1] = hex[(p_data[i]   )&0xf];
-    }
-    s[2*i_data] = '\0';
-}
-
 /**
  * Shrink the MTU down to a fixed packetization time (for audio).
  */
@@ -881,8 +917,8 @@ static void
 rtp_set_ptime (sout_stream_id_t *id, unsigned ptime_ms, size_t bytes)
 {
     /* Samples per second */
-    size_t spl = (id->i_clock_rate - 1) * ptime_ms / 1000 + 1;
-    bytes *= id->i_channels;
+    size_t spl = (id->rtp_fmt.clock_rate - 1) * ptime_ms / 1000 + 1;
+    bytes *= id->rtp_fmt.channels;
     spl *= bytes;
 
     if (spl < rtp_mtu (id)) /* MTU is big enough for ptime */
@@ -891,11 +927,11 @@ rtp_set_ptime (sout_stream_id_t *id, unsigned ptime_ms, size_t bytes)
         id->i_mtu = 12 + (((id->i_mtu - 12) / bytes) * bytes);
 }
 
-uint32_t rtp_compute_ts( const sout_stream_id_t *id, int64_t i_pts )
+uint32_t rtp_compute_ts( unsigned i_clock_rate, int64_t i_pts )
 {
     /* NOTE: this plays nice with offsets because the calculations are
      * linear. */
-    return i_pts * (int64_t)id->i_clock_rate / CLOCK_FREQ;
+    return i_pts * (int64_t)i_clock_rate / CLOCK_FREQ;
 }
 
 /** Add an ES as a new RTP stream */
@@ -917,33 +953,6 @@ static sout_stream_id_t *Add( sout_stream_t *p_stream, es_format_t *p_fmt )
         return NULL;
     id->p_stream   = p_stream;
 
-    /* Look for free dymanic payload type */
-    id->i_payload_type = 96 + clz32 (p_sys->payload_bitmap);
-    assert (id->i_payload_type < 128);
-
-    vlc_rand_bytes (&id->i_sequence, sizeof (id->i_sequence));
-    vlc_rand_bytes (id->ssrc, sizeof (id->ssrc));
-
-    id->psz_enc    = NULL;
-    id->psz_fmtp   = NULL;
-    id->i_clock_rate = 90000; /* most common case for video */
-    id->i_channels = 0;
-    if( p_fmt != NULL )
-    {
-        id->i_cat  = p_fmt->i_cat;
-        if( p_fmt->i_cat == AUDIO_ES )
-        {
-            id->i_clock_rate = p_fmt->audio.i_rate;
-            id->i_channels = p_fmt->audio.i_channels;
-        }
-        id->i_bitrate = p_fmt->i_bitrate/1000; /* Stream bitrate in kbps */
-    }
-    else
-    {
-        id->i_cat  = VIDEO_ES;
-        id->i_bitrate = 0;
-    }
-
     id->i_mtu = var_InheritInteger( p_stream, "mtu" );
     if( id->i_mtu <= 12 + 16 )
         id->i_mtu = 576 - 20 - 8; /* pessimistic */
@@ -964,6 +973,57 @@ static sout_stream_id_t *Add( sout_stream_t *p_stream, es_format_t *p_fmt )
     id->i_caching =
         (int64_t)1000 * var_GetInteger( p_stream, SOUT_CFG_PREFIX "caching");
 
+    vlc_rand_bytes (&id->i_sequence, sizeof (id->i_sequence));
+    vlc_rand_bytes (id->ssrc, sizeof (id->ssrc));
+
+    id->rtp_fmt.ptname = NULL;
+
+    if (p_sys->p_vod_media != NULL)
+    {
+        uint32_t ssrc;
+        int val = vod_init_id(p_sys->p_vod_media, p_sys->psz_vod_session,
+                              p_fmt ? p_fmt->i_id : 0, id, &id->rtp_fmt,
+                              &ssrc, &id->i_seq_sent_next);
+        if (val == VLC_SUCCESS)
+        {
+            memcpy(id->ssrc, &ssrc, sizeof(id->ssrc));
+            /* This is ugly, but id->i_seq_sent_next needs to be
+             * initialized inside vod_init_id() to avoid race
+             * conditions. */
+            id->i_sequence = id->i_seq_sent_next;
+        }
+
+        /* XXX: It might happen that the VoD media gives us a dynamic
+         * payload type that is already in use in p_sys->payload_bitmap,
+         * because it was taken by a previous ES that was missing in
+         * the VoD media. */
+
+        /* vod_init_id() may fail either because the ES wasn't found in
+         * the VoD media, or because that track wasn't SETUP. In the
+         * former case, id->rtp_fmt was left untouched, so we fill it
+         * in right after this. */
+    }
+
+    if (id->rtp_fmt.ptname == NULL)
+    {
+        char *psz = var_GetNonEmptyString( p_stream, SOUT_CFG_PREFIX "mux" );
+        if (p_fmt == NULL && psz == NULL)
+            goto error;
+        rtp_get_fmt(VLC_OBJECT(p_stream), p_fmt, psz, &id->rtp_fmt);
+        free( psz );
+
+        if (id->rtp_fmt.payload_type >= 96)
+        {
+            /* Look for free dymanic payload type */
+            id->rtp_fmt.payload_type = 96 + clz32 (p_sys->payload_bitmap);
+            assert (id->rtp_fmt.payload_type < 128);
+        }
+    }
+
+    if (id->rtp_fmt.payload_type >= 96)
+        /* Mark dynamic payload type in use */
+        p_sys->payload_bitmap &= ~(1 << (127 - id->rtp_fmt.payload_type));
+
 #ifdef HAVE_SRTP
     char *key = var_CreateGetNonEmptyString (p_stream, SOUT_CFG_PREFIX"key");
     if (key)
@@ -1034,7 +1094,7 @@ static sout_stream_id_t *Add( sout_stream_t *p_stream, es_format_t *p_fmt )
             case IPPROTO_DCCP:
             {
                 const char *code;
-                switch (id->i_cat)
+                switch (id->rtp_fmt.cat)
                 {
                     case VIDEO_ES: code = "RTPV";     break;
                     case AUDIO_ES: code = "RTPARTPV"; break;
@@ -1082,92 +1142,42 @@ static sout_stream_id_t *Add( sout_stream_t *p_stream, es_format_t *p_fmt )
         }
     }
 
-    if( p_fmt == NULL )
-    {
-        char *psz = var_GetNonEmptyString( p_stream, SOUT_CFG_PREFIX "mux" );
-
-        if( psz == NULL ) /* Uho! */
-            ;
-        else
-        if( strncmp( psz, "ts", 2 ) == 0 )
-        {
-            id->i_payload_type = 33;
-            id->psz_enc = "MP2T";
-        }
-        else
-        {
-            id->psz_enc = "MP2P";
-        }
-        free( psz );
-    }
-    else
+    if( p_fmt != NULL )
     switch( p_fmt->i_codec )
     {
         case VLC_CODEC_MULAW:
-            if( p_fmt->audio.i_channels == 1 && p_fmt->audio.i_rate == 8000 )
-                id->i_payload_type = 0;
-            id->psz_enc = "PCMU";
-            id->pf_packetize = rtp_packetize_split;
-            rtp_set_ptime (id, 20, 1);
-            break;
         case VLC_CODEC_ALAW:
-            if( p_fmt->audio.i_channels == 1 && p_fmt->audio.i_rate == 8000 )
-                id->i_payload_type = 8;
-            id->psz_enc = "PCMA";
+        case VLC_CODEC_U8:
             id->pf_packetize = rtp_packetize_split;
             rtp_set_ptime (id, 20, 1);
             break;
         case VLC_CODEC_S16B:
         case VLC_CODEC_S16L:
-            if( p_fmt->audio.i_channels == 1 && p_fmt->audio.i_rate == 44100 )
-            {
-                id->i_payload_type = 11;
-            }
-            else if( p_fmt->audio.i_channels == 2 &&
-                     p_fmt->audio.i_rate == 44100 )
-            {
-                id->i_payload_type = 10;
-            }
-            id->psz_enc = "L16";
             if( p_fmt->i_codec == VLC_CODEC_S16B )
                 id->pf_packetize = rtp_packetize_split;
             else
                 id->pf_packetize = rtp_packetize_swab;
             rtp_set_ptime (id, 20, 2);
             break;
-        case VLC_CODEC_U8:
-            id->psz_enc = "L8";
-            id->pf_packetize = rtp_packetize_split;
-            rtp_set_ptime (id, 20, 1);
-            break;
         case VLC_CODEC_MPGA:
-            id->i_payload_type = 14;
-            id->psz_enc = "MPA";
-            id->i_clock_rate = 90000; /* not 44100 */
             id->pf_packetize = rtp_packetize_mpa;
             break;
         case VLC_CODEC_MPGV:
-            id->i_payload_type = 32;
-            id->psz_enc = "MPV";
             id->pf_packetize = rtp_packetize_mpv;
             break;
         case VLC_CODEC_ADPCM_G726:
             switch( p_fmt->i_bitrate / 1000 )
             {
             case 16:
-                id->psz_enc = "G726-16";
                 id->pf_packetize = rtp_packetize_g726_16;
                 break;
             case 24:
-                id->psz_enc = "G726-24";
                 id->pf_packetize = rtp_packetize_g726_24;
                 break;
             case 32:
-                id->psz_enc = "G726-32";
                 id->pf_packetize = rtp_packetize_g726_32;
                 break;
             case 40:
-                id->psz_enc = "G726-40";
                 id->pf_packetize = rtp_packetize_g726_40;
                 break;
             default:
@@ -1177,148 +1187,33 @@ static sout_stream_id_t *Add( sout_stream_t *p_stream, es_format_t *p_fmt )
             }
             break;
         case VLC_CODEC_A52:
-            id->psz_enc = "ac3";
             id->pf_packetize = rtp_packetize_ac3;
             break;
         case VLC_CODEC_H263:
-            id->psz_enc = "H263-1998";
             id->pf_packetize = rtp_packetize_h263;
             break;
         case VLC_CODEC_H264:
-            id->psz_enc = "H264";
             id->pf_packetize = rtp_packetize_h264;
-            id->psz_fmtp = NULL;
-
-            if( p_fmt->i_extra > 0 )
-            {
-                uint8_t *p_buffer = p_fmt->p_extra;
-                int     i_buffer = p_fmt->i_extra;
-                char    *p_64_sps = NULL;
-                char    *p_64_pps = NULL;
-                char    hexa[6+1];
-
-                while( i_buffer > 4 &&
-                       p_buffer[0] == 0 && p_buffer[1] == 0 &&
-                       p_buffer[2] == 0 && p_buffer[3] == 1 )
-                {
-                    const int i_nal_type = p_buffer[4]&0x1f;
-                    int i_offset;
-                    int i_size      = 0;
-
-                    msg_Dbg( p_stream, "we found a startcode for NAL with TYPE:%d", i_nal_type );
-
-                    i_size = i_buffer;
-                    for( i_offset = 4; i_offset+3 < i_buffer ; i_offset++)
-                    {
-                        if( !memcmp (p_buffer + i_offset, "\x00\x00\x00\x01", 4 ) )
-                        {
-                            /* we found another startcode */
-                            i_size = i_offset;
-                            break;
-                        }
-                    }
-                    if( i_nal_type == 7 )
-                    {
-                        p_64_sps = vlc_b64_encode_binary( &p_buffer[4], i_size - 4 );
-                        sprintf_hexa( hexa, &p_buffer[5], 3 );
-                    }
-                    else if( i_nal_type == 8 )
-                    {
-                        p_64_pps = vlc_b64_encode_binary( &p_buffer[4], i_size - 4 );
-                    }
-                    i_buffer -= i_size;
-                    p_buffer += i_size;
-                }
-                /* */
-                if( p_64_sps && p_64_pps &&
-                    ( asprintf( &id->psz_fmtp,
-                                "packetization-mode=1;profile-level-id=%s;"
-                                "sprop-parameter-sets=%s,%s;", hexa, p_64_sps,
-                                p_64_pps ) == -1 ) )
-                    id->psz_fmtp = NULL;
-                free( p_64_sps );
-                free( p_64_pps );
-            }
-            if( !id->psz_fmtp )
-                id->psz_fmtp = strdup( "packetization-mode=1" );
             break;
-
         case VLC_CODEC_MP4V:
-        {
-            id->psz_enc = "MP4V-ES";
             id->pf_packetize = rtp_packetize_split;
-            if( p_fmt->i_extra > 0 )
-            {
-                char hexa[2*p_fmt->i_extra +1];
-                sprintf_hexa( hexa, p_fmt->p_extra, p_fmt->i_extra );
-                if( asprintf( &id->psz_fmtp,
-                              "profile-level-id=3; config=%s;", hexa ) == -1 )
-                    id->psz_fmtp = NULL;
-            }
             break;
-        }
         case VLC_CODEC_MP4A:
         {
             if(!p_sys->b_latm)
-            {
-                char hexa[2*p_fmt->i_extra +1];
-
-                id->psz_enc = "mpeg4-generic";
                 id->pf_packetize = rtp_packetize_mp4a;
-                sprintf_hexa( hexa, p_fmt->p_extra, p_fmt->i_extra );
-                if( asprintf( &id->psz_fmtp,
-                              "streamtype=5; profile-level-id=15; "
-                              "mode=AAC-hbr; config=%s; SizeLength=13; "
-                              "IndexLength=3; IndexDeltaLength=3; Profile=1;",
-                              hexa ) == -1 )
-                    id->psz_fmtp = NULL;
-            }
             else
-            {
-                char hexa[13];
-                int i;
-                unsigned char config[6];
-                unsigned int aacsrates[15] = {
-                    96000, 88200, 64000, 48000, 44100, 32000, 24000, 22050,
-                    16000, 12000, 11025, 8000, 7350, 0, 0 };
-
-                for( i = 0; i < 15; i++ )
-                    if( p_fmt->audio.i_rate == aacsrates[i] )
-                        break;
-
-                config[0]=0x40;
-                config[1]=0;
-                config[2]=0x20|i;
-                config[3]=p_fmt->audio.i_channels<<4;
-                config[4]=0x3f;
-                config[5]=0xc0;
-
-                id->psz_enc = "MP4A-LATM";
                 id->pf_packetize = rtp_packetize_mp4a_latm;
-                sprintf_hexa( hexa, config, 6 );
-                if( asprintf( &id->psz_fmtp, "profile-level-id=15; "
-                              "object=2; cpresent=0; config=%s", hexa ) == -1 )
-                    id->psz_fmtp = NULL;
-            }
             break;
         }
         case VLC_CODEC_AMR_NB:
-            id->psz_enc = "AMR";
-            id->psz_fmtp = strdup( "octet-align=1" );
-            id->pf_packetize = rtp_packetize_amr;
-            break;
         case VLC_CODEC_AMR_WB:
-            id->psz_enc = "AMR-WB";
-            id->psz_fmtp = strdup( "octet-align=1" );
             id->pf_packetize = rtp_packetize_amr;
             break;
         case VLC_CODEC_SPEEX:
-            id->psz_enc = "SPEEX";
             id->pf_packetize = rtp_packetize_spx;
             break;
         case VLC_CODEC_ITU_T140:
-            id->psz_enc = "t140" ;
-            id->i_clock_rate = 1000;
             id->pf_packetize = rtp_packetize_t140;
             break;
 
@@ -1327,9 +1222,6 @@ static sout_stream_id_t *Add( sout_stream_t *p_stream, es_format_t *p_fmt )
                      "codec: %4.4s)", (char*)&p_fmt->i_codec );
             goto error;
     }
-    if (id->i_payload_type >= 96)
-        /* Mark dynamic payload type in use */
-        p_sys->payload_bitmap &= ~(1 << (127 - id->i_payload_type));
 
 #if 0 /* No payload formats sets this at the moment */
     int cscov = -1;
@@ -1343,11 +1235,13 @@ static sout_stream_id_t *Add( sout_stream_t *p_stream, es_format_t *p_fmt )
     id->b_ts_init = ( p_sys->i_npt_zero != VLC_TS_INVALID );
     vlc_mutex_unlock( &p_sys->lock_ts );
     if( id->b_ts_init )
-        id->i_ts_offset = rtp_compute_ts( id, p_sys->i_pts_offset );
+        id->i_ts_offset = rtp_compute_ts( id->rtp_fmt.clock_rate,
+                                          p_sys->i_pts_offset );
 
     if( p_sys->rtsp != NULL )
         id->rtsp_id = RtspAddId( p_sys->rtsp, id,
                                  GetDWBE( id->ssrc ),
+                                 id->rtp_fmt.clock_rate,
                                  p_sys->psz_destination,
                                  p_sys->i_ttl, id->i_port, id->i_port + 1 );
 
@@ -1402,11 +1296,13 @@ static int Del( sout_stream_t *p_stream, sout_stream_id_t *id )
     }
 
     /* Release dynamic payload type */
-    if (id->i_payload_type >= 96)
-        p_sys->payload_bitmap |= 1 << (127 - id->i_payload_type);
+    if (id->rtp_fmt.payload_type >= 96)
+        p_sys->payload_bitmap |= 1 << (127 - id->rtp_fmt.payload_type);
 
-    free( id->psz_fmtp );
+    free( id->rtp_fmt.fmtp );
 
+    if (p_sys->p_vod_media != NULL)
+        vod_detach_id(p_sys->p_vod_media, p_sys->psz_vod_session, id);
     if( id->rtsp_id )
         RtspDelId( p_sys->rtsp, id->rtsp_id );
     if( id->listen.fd != NULL )
@@ -1753,6 +1649,11 @@ int64_t rtp_get_ts( const sout_stream_t *p_stream )
     return p_sys->i_pts_zero + (now - i_npt_zero); 
 }
 
+sout_stream_t *rtp_get_pstream( const sout_stream_id_t *id )
+{
+    return id->p_stream; 
+}
+
 void rtp_packetize_common( sout_stream_id_t *id, block_t *out,
                            int b_marker, int64_t i_pts )
 {
@@ -1772,14 +1673,16 @@ void rtp_packetize_common( sout_stream_id_t *id, block_t *out,
 
         /* And in any case this is the first packet of this ES, so we
          * initialize the offset for this ES. */
-        id->i_ts_offset = rtp_compute_ts( id, p_sys->i_pts_offset );
+        id->i_ts_offset = rtp_compute_ts( id->rtp_fmt.clock_rate,
+                                          p_sys->i_pts_offset );
         id->b_ts_init = true;
     }
 
-    uint32_t i_timestamp = rtp_compute_ts( id, i_pts ) + id->i_ts_offset;
+    uint32_t i_timestamp = rtp_compute_ts( id->rtp_fmt.clock_rate, i_pts )
+                           + id->i_ts_offset;
 
     out->p_buffer[0] = 0x80;
-    out->p_buffer[1] = (b_marker?0x80:0x00)|id->i_payload_type;
+    out->p_buffer[1] = (b_marker?0x80:0x00)|id->rtp_fmt.payload_type;
     out->p_buffer[2] = ( id->i_sequence >> 8)&0xff;
     out->p_buffer[3] = ( id->i_sequence     )&0xff;
     out->p_buffer[4] = ( i_timestamp >> 24 )&0xff;
diff --git a/modules/stream_out/rtp.h b/modules/stream_out/rtp.h
index 01e7288..00c39d2 100644
--- a/modules/stream_out/rtp.h
+++ b/modules/stream_out/rtp.h
@@ -25,24 +25,34 @@
 typedef struct rtsp_stream_t rtsp_stream_t;
 typedef struct rtsp_stream_id_t rtsp_stream_id_t;
 
-rtsp_stream_t *RtspSetup( sout_stream_t *p_stream, const vlc_url_t *url );
+rtsp_stream_t *RtspSetup( vlc_object_t *owner, vod_media_t *media,
+                          const vlc_url_t *url );
 void RtspUnsetup( rtsp_stream_t *rtsp );
 
 rtsp_stream_id_t *RtspAddId( rtsp_stream_t *rtsp, sout_stream_id_t *sid,
-                             uint32_t ssrc,
+                             uint32_t ssrc, unsigned clock_rate,
                              const char *dst, int ttl,
                              unsigned loport, unsigned hiport );
 void RtspDelId( rtsp_stream_t *rtsp, rtsp_stream_id_t * );
 
 char *RtspAppendTrackPath( rtsp_stream_id_t *id, const char *base );
 
+int RtspClientLookup( rtsp_stream_t *rtsp, const char *name, int64_t *ts_init );
+int RtspTrackAttach( rtsp_stream_t *rtsp, const char *name,
+                     rtsp_stream_id_t *id, sout_stream_id_t *sout_id,
+                     uint32_t *ssrc, uint16_t *seq_init );
+void RtspTrackDetach( rtsp_stream_t *rtsp, const char *name,
+                      sout_stream_id_t *sout_id);
+
 char *SDPGenerate( sout_stream_t *p_stream, const char *rtsp_url );
+char *SDPGenerateVoD( const vod_media_t *p_media, const char *rtsp_url );
 
-uint32_t rtp_compute_ts( const sout_stream_id_t *id, int64_t i_pts );
+uint32_t rtp_compute_ts( unsigned i_clock_rate, int64_t i_pts );
 int rtp_add_sink( sout_stream_id_t *id, int fd, bool rtcp_mux, uint16_t *seq );
 void rtp_del_sink( sout_stream_id_t *id, int fd );
 uint16_t rtp_get_seq( sout_stream_id_t *id );
 int64_t rtp_get_ts( const sout_stream_t *p_stream );
+sout_stream_t *rtp_get_pstream( const sout_stream_id_t *id );
 
 /* RTP packetization */
 void rtp_packetize_common (sout_stream_id_t *id, block_t *out,
@@ -73,3 +83,37 @@ rtcp_sender_t *OpenRTCP (vlc_object_t *obj, int rtp_fd, int proto,
                          bool mux);
 void CloseRTCP (rtcp_sender_t *rtcp);
 void SendRTCP (rtcp_sender_t *restrict rtcp, const block_t *rtp);
+
+typedef struct rtp_format_t
+{
+    /* Used for SDP and packetization */
+    uint8_t      payload_type;
+    unsigned     clock_rate;
+    unsigned     channels;
+    int          cat;
+    /* Used in SDP only */
+    unsigned     bitrate;
+    const char  *ptname;
+    char        *fmtp;
+} rtp_format_t;
+
+void rtp_get_fmt( vlc_object_t *obj, es_format_t *p_fmt, const char *mux,
+                  rtp_format_t *p_rtp_fmt );
+
+/* VoD */
+int  OpenVoD ( vlc_object_t * );
+void CloseVoD( vlc_object_t * );
+
+int vod_start(vod_media_t *p_media, const char *psz_session);
+void vod_toggle_pause(vod_media_t *p_media, const char *psz_session);
+void vod_stop(vod_media_t *p_media, const char *psz_session);
+void vod_seek(vod_media_t *p_media, const char *psz_session, float time);
+
+vod_media_t *vod_get_session(sout_stream_t *p_stream, const char *psz_session,
+                             const char **mux, int64_t *i_ts_init);
+int vod_init_id(vod_media_t *p_media, const char *psz_session, int es_id,
+                sout_stream_id_t *sout_id, rtp_format_t *rtp_fmt,
+                uint32_t *ssrc, uint16_t *seq_init);
+void vod_detach_id(vod_media_t *p_media, const char *psz_session,
+                   sout_stream_id_t *sout_id);
+
diff --git a/modules/stream_out/rtpfmt.c b/modules/stream_out/rtpfmt.c
index 482db7d..001bebd 100644
--- a/modules/stream_out/rtpfmt.c
+++ b/modules/stream_out/rtpfmt.c
@@ -29,9 +29,256 @@
 #include <vlc_common.h>
 #include <vlc_sout.h>
 #include <vlc_block.h>
+#include <vlc_strings.h>
 
 #include "rtp.h"
 
+#include <assert.h>
+
+static void sprintf_hexa( char *s, uint8_t *p_data, int i_data )
+{
+    static const char hex[16] = "0123456789abcdef";
+
+    for( int i = 0; i < i_data; i++ )
+    {
+        s[2*i+0] = hex[(p_data[i]>>4)&0xf];
+        s[2*i+1] = hex[(p_data[i]   )&0xf];
+    }
+    s[2*i_data] = '\0';
+}
+
+void rtp_get_fmt( vlc_object_t *obj, es_format_t *p_fmt, const char *mux,
+                  rtp_format_t *rtp_fmt )
+{
+    assert( p_fmt != NULL || mux != NULL );
+
+    rtp_fmt->payload_type = 96; /* dynamic payload type */
+    rtp_fmt->ptname = NULL;
+    rtp_fmt->cat = mux != NULL ? VIDEO_ES : p_fmt->i_cat;
+    if( rtp_fmt->cat == AUDIO_ES )
+    {
+        rtp_fmt->clock_rate = p_fmt->audio.i_rate;
+        rtp_fmt->channels = p_fmt->audio.i_channels;
+    }
+    else
+        rtp_fmt->clock_rate = 90000; /* most common case for video */
+    /* Stream bitrate in kbps */
+    rtp_fmt->bitrate = p_fmt != NULL ? p_fmt->i_bitrate/1000 : 0;
+    rtp_fmt->fmtp = NULL;
+
+    if( mux != NULL )
+    {
+        if( strncmp( mux, "ts", 2 ) == 0 )
+        {
+            rtp_fmt->payload_type = 33;
+            rtp_fmt->ptname = "MP2T";
+        }
+        else
+            rtp_fmt->ptname = "MP2P";
+        return;
+    }
+
+    switch( p_fmt->i_codec )
+    {
+        case VLC_CODEC_MULAW:
+            if( p_fmt->audio.i_channels == 1 && p_fmt->audio.i_rate == 8000 )
+                rtp_fmt->payload_type = 0;
+            rtp_fmt->ptname = "PCMU";
+            break;
+        case VLC_CODEC_ALAW:
+            if( p_fmt->audio.i_channels == 1 && p_fmt->audio.i_rate == 8000 )
+                rtp_fmt->payload_type = 8;
+            rtp_fmt->ptname = "PCMA";
+            break;
+        case VLC_CODEC_S16B:
+        case VLC_CODEC_S16L:
+            if( p_fmt->audio.i_channels == 1 && p_fmt->audio.i_rate == 44100 )
+            {
+                rtp_fmt->payload_type = 11;
+            }
+            else if( p_fmt->audio.i_channels == 2 &&
+                     p_fmt->audio.i_rate == 44100 )
+            {
+                rtp_fmt->payload_type = 10;
+            }
+            rtp_fmt->ptname = "L16";
+            break;
+        case VLC_CODEC_U8:
+            rtp_fmt->ptname = "L8";
+            break;
+        case VLC_CODEC_MPGA:
+            rtp_fmt->payload_type = 14;
+            rtp_fmt->ptname = "MPA";
+            rtp_fmt->clock_rate = 90000; /* not 44100 */
+            break;
+        case VLC_CODEC_MPGV:
+            rtp_fmt->payload_type = 32;
+            rtp_fmt->ptname = "MPV";
+            break;
+        case VLC_CODEC_ADPCM_G726:
+            switch( p_fmt->i_bitrate / 1000 )
+            {
+            case 16:
+                rtp_fmt->ptname = "G726-16";
+                break;
+            case 24:
+                rtp_fmt->ptname = "G726-24";
+                break;
+            case 32:
+                rtp_fmt->ptname = "G726-32";
+                break;
+            case 40:
+                rtp_fmt->ptname = "G726-40";
+                break;
+            default:
+                msg_Err( obj, "cannot add this stream (unsupported "
+                         "G.726 bit rate: %u)", p_fmt->i_bitrate );
+                return;
+            }
+            break;
+        case VLC_CODEC_A52:
+            rtp_fmt->ptname = "ac3";
+            break;
+        case VLC_CODEC_H263:
+            rtp_fmt->ptname = "H263-1998";
+            break;
+        case VLC_CODEC_H264:
+            rtp_fmt->ptname = "H264";
+            rtp_fmt->fmtp = NULL;
+
+            if( p_fmt->i_extra > 0 )
+            {
+                uint8_t *p_buffer = p_fmt->p_extra;
+                int     i_buffer = p_fmt->i_extra;
+                char    *p_64_sps = NULL;
+                char    *p_64_pps = NULL;
+                char    hexa[6+1];
+
+                while( i_buffer > 4 &&
+                       p_buffer[0] == 0 && p_buffer[1] == 0 &&
+                       p_buffer[2] == 0 && p_buffer[3] == 1 )
+                {
+                    const int i_nal_type = p_buffer[4]&0x1f;
+                    int i_offset;
+                    int i_size      = 0;
+
+                    msg_Dbg( obj, "we found a startcode for NAL with TYPE:%d", i_nal_type );
+
+                    i_size = i_buffer;
+                    for( i_offset = 4; i_offset+3 < i_buffer ; i_offset++)
+                    {
+                        if( !memcmp (p_buffer + i_offset, "\x00\x00\x00\x01", 4 ) )
+                        {
+                            /* we found another startcode */
+                            i_size = i_offset;
+                            break;
+                        }
+                    }
+                    if( i_nal_type == 7 )
+                    {
+                        p_64_sps = vlc_b64_encode_binary( &p_buffer[4], i_size - 4 );
+                        sprintf_hexa( hexa, &p_buffer[5], 3 );
+                    }
+                    else if( i_nal_type == 8 )
+                    {
+                        p_64_pps = vlc_b64_encode_binary( &p_buffer[4], i_size - 4 );
+                    }
+                    i_buffer -= i_size;
+                    p_buffer += i_size;
+                }
+                /* */
+                if( p_64_sps && p_64_pps &&
+                    ( asprintf( &rtp_fmt->fmtp,
+                                "packetization-mode=1;profile-level-id=%s;"
+                                "sprop-parameter-sets=%s,%s;", hexa, p_64_sps,
+                                p_64_pps ) == -1 ) )
+                    rtp_fmt->fmtp = NULL;
+                free( p_64_sps );
+                free( p_64_pps );
+            }
+            if( rtp_fmt->fmtp == NULL )
+                rtp_fmt->fmtp = strdup( "packetization-mode=1" );
+            break;
+
+        case VLC_CODEC_MP4V:
+        {
+            rtp_fmt->ptname = "MP4V-ES";
+            if( p_fmt->i_extra > 0 )
+            {
+                char hexa[2*p_fmt->i_extra +1];
+                sprintf_hexa( hexa, p_fmt->p_extra, p_fmt->i_extra );
+                if( asprintf( &rtp_fmt->fmtp,
+                              "profile-level-id=3; config=%s;", hexa ) == -1 )
+                    rtp_fmt->fmtp = NULL;
+            }
+            break;
+        }
+        case VLC_CODEC_MP4A:
+        {
+            if( ! var_InheritBool( obj, "sout-rtp-mp4a-latm" ) )
+            {
+                char hexa[2*p_fmt->i_extra +1];
+
+                rtp_fmt->ptname = "mpeg4-generic";
+                sprintf_hexa( hexa, p_fmt->p_extra, p_fmt->i_extra );
+                if( asprintf( &rtp_fmt->fmtp,
+                              "streamtype=5; profile-level-id=15; "
+                              "mode=AAC-hbr; config=%s; SizeLength=13; "
+                              "IndexLength=3; IndexDeltaLength=3; Profile=1;",
+                              hexa ) == -1 )
+                    rtp_fmt->fmtp = NULL;
+            }
+            else
+            {
+                char hexa[13];
+                int i;
+                unsigned char config[6];
+                unsigned int aacsrates[15] = {
+                    96000, 88200, 64000, 48000, 44100, 32000, 24000, 22050,
+                    16000, 12000, 11025, 8000, 7350, 0, 0 };
+
+                for( i = 0; i < 15; i++ )
+                    if( p_fmt->audio.i_rate == aacsrates[i] )
+                        break;
+
+                config[0]=0x40;
+                config[1]=0;
+                config[2]=0x20|i;
+                config[3]=p_fmt->audio.i_channels<<4;
+                config[4]=0x3f;
+                config[5]=0xc0;
+
+                rtp_fmt->ptname = "MP4A-LATM";
+                sprintf_hexa( hexa, config, 6 );
+                if( asprintf( &rtp_fmt->fmtp, "profile-level-id=15; "
+                              "object=2; cpresent=0; config=%s", hexa ) == -1 )
+                    rtp_fmt->fmtp = NULL;
+            }
+            break;
+        }
+        case VLC_CODEC_AMR_NB:
+            rtp_fmt->ptname = "AMR";
+            rtp_fmt->fmtp = strdup( "octet-align=1" );
+            break;
+        case VLC_CODEC_AMR_WB:
+            rtp_fmt->ptname = "AMR-WB";
+            rtp_fmt->fmtp = strdup( "octet-align=1" );
+            break;
+        case VLC_CODEC_SPEEX:
+            rtp_fmt->ptname = "SPEEX";
+            break;
+        case VLC_CODEC_ITU_T140:
+            rtp_fmt->ptname = "t140" ;
+            rtp_fmt->clock_rate = 1000;
+            break;
+
+        default:
+            msg_Err( obj, "cannot add this stream (unsupported "
+                     "codec: %4.4s)", (char*)&p_fmt->i_codec );
+    }
+}
+
+
 int
 rtp_packetize_h264_nal( sout_stream_id_t *id,
                         const uint8_t *p_data, int i_data, int64_t i_pts,
diff --git a/modules/stream_out/rtsp.c b/modules/stream_out/rtsp.c
index a1cc953..fc57def 100644
--- a/modules/stream_out/rtsp.c
+++ b/modules/stream_out/rtsp.c
@@ -1,12 +1,13 @@
 /*****************************************************************************
  * rtsp.c: RTSP support for RTP stream output module
  *****************************************************************************
- * Copyright (C) 2003-2004 the VideoLAN team
+ * Copyright (C) 2003-2004, 2010 the VideoLAN team
  * Copyright © 2007 Rémi Denis-Courmont
  *
  * $Id$
  *
  * Authors: Laurent Aimar <fenrir at via.ecp.fr>
+ *          Pierre Ynard
  *
  * 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
@@ -35,12 +36,21 @@
 
 #include <vlc_httpd.h>
 #include <vlc_url.h>
+#include <vlc_charset.h>
+#include <vlc_fs.h>
 #include <vlc_network.h>
 #include <vlc_rand.h>
 #include <assert.h>
 #include <errno.h>
 #include <stdlib.h>
 
+#ifndef WIN32
+# include <locale.h>
+#endif
+#ifdef HAVE_XLOCALE_H
+# include <xlocale.h>
+#endif
+
 #include "rtp.h"
 
 typedef struct rtsp_session_t rtsp_session_t;
@@ -48,7 +58,8 @@ typedef struct rtsp_session_t rtsp_session_t;
 struct rtsp_stream_t
 {
     vlc_mutex_t     lock;
-    sout_stream_t  *owner;
+    vlc_object_t   *owner;
+    vod_media_t    *vod_media;
     httpd_host_t   *host;
     httpd_url_t    *url;
     char           *psz_path;
@@ -68,7 +79,8 @@ static int  RtspCallbackId( httpd_callback_sys_t *p_args,
                             const httpd_message_t *query );
 static void RtspClientDel( rtsp_stream_t *rtsp, rtsp_session_t *session );
 
-rtsp_stream_t *RtspSetup( sout_stream_t *p_stream, const vlc_url_t *url )
+rtsp_stream_t *RtspSetup( vlc_object_t *owner, vod_media_t *media,
+                          const vlc_url_t *url )
 {
     rtsp_stream_t *rtsp = malloc( sizeof( *rtsp ) );
 
@@ -78,7 +90,8 @@ rtsp_stream_t *RtspSetup( sout_stream_t *p_stream, const vlc_url_t *url )
         return NULL;
     }
 
-    rtsp->owner = p_stream;
+    rtsp->owner = owner;
+    rtsp->vod_media = media;
     rtsp->sessionc = 0;
     rtsp->sessionv = NULL;
     rtsp->host = NULL;
@@ -92,10 +105,10 @@ rtsp_stream_t *RtspSetup( sout_stream_t *p_stream, const vlc_url_t *url )
     if( rtsp->psz_path == NULL )
         goto error;
 
-    msg_Dbg( p_stream, "RTSP stream: host %s port %d at %s",
+    msg_Dbg( owner, "RTSP stream: host %s port %d at %s",
              url->psz_host, rtsp->port, rtsp->psz_path );
 
-    rtsp->host = httpd_HostNew( VLC_OBJECT(p_stream), url->psz_host,
+    rtsp->host = httpd_HostNew( VLC_OBJECT(owner), url->psz_host,
                                 rtsp->port );
     if( rtsp->host == NULL )
         goto error;
@@ -142,6 +155,7 @@ struct rtsp_stream_id_t
 {
     rtsp_stream_t    *stream;
     sout_stream_id_t *sout_id;
+    unsigned          clock_rate; /* needed to compute rtptime in RTP-Info */
     httpd_url_t      *url;
     const char       *dst;
     int               ttl;
@@ -158,6 +172,9 @@ struct rtsp_session_t
 {
     rtsp_stream_t *stream;
     uint64_t       id;
+    int64_t        ts_init;
+    bool           vod_started; /* true if the VoD media instance was created */
+    bool           paused;      /* true if the client paused the VoD instance */
 
     /* output (id-access) */
     int            trackc;
@@ -169,10 +186,15 @@ struct rtsp_session_t
 struct rtsp_strack_t
 {
     rtsp_stream_id_t  *id;
-    int                fd;
+    sout_stream_id_t  *sout_id;
+    int          setup_fd;       /* socket created by the SETUP request */
+    int          rtp_fd;         /* socket used by the RTP output */
+    uint32_t     ssrc;
+    uint16_t     seq_init;
     bool         playing;
 };
 
+static void RtspTrackClose( rtsp_strack_t *tr );
 
 char *RtspAppendTrackPath( rtsp_stream_id_t *id, const char *base )
 {
@@ -187,7 +209,7 @@ char *RtspAppendTrackPath( rtsp_stream_id_t *id, const char *base )
 
 
 rtsp_stream_id_t *RtspAddId( rtsp_stream_t *rtsp, sout_stream_id_t *sid,
-                             uint32_t ssrc,
+                             uint32_t ssrc, unsigned clock_rate,
                              /* Multicast stuff - TODO: cleanup */
                              const char *dst, int ttl,
                              unsigned loport, unsigned hiport )
@@ -203,6 +225,7 @@ rtsp_stream_id_t *RtspAddId( rtsp_stream_t *rtsp, sout_stream_id_t *sid,
     id->sout_id = sid;
     id->track_id = rtsp->track_id;
     id->ssrc = ssrc;
+    id->clock_rate = clock_rate;
     /* TODO: can we assume that this need not be strdup'd? */
     id->dst = dst;
     if( id->dst != NULL )
@@ -256,7 +279,7 @@ void RtspDelId( rtsp_stream_t *rtsp, rtsp_stream_id_t *id )
             if( ses->trackv[j].id == id )
             {
                 rtsp_strack_t *tr = ses->trackv + j;
-                rtp_del_sink( tr->id->sout_id, tr->fd );
+                RtspTrackClose( tr );
                 REMOVE_ELEM( ses->trackv, ses->trackc, j );
             }
         }
@@ -277,6 +300,9 @@ rtsp_session_t *RtspClientNew( rtsp_stream_t *rtsp )
 
     s->stream = rtsp;
     vlc_rand_bytes (&s->id, sizeof (s->id));
+    s->ts_init = mdate(); /* arbitrary value, could probably be random */
+    s->vod_started = false;
+    s->paused = false;
     s->trackc = 0;
     s->trackv = NULL;
 
@@ -320,12 +346,126 @@ void RtspClientDel( rtsp_stream_t *rtsp, rtsp_session_t *session )
     TAB_REMOVE( rtsp->sessionc, rtsp->sessionv, session );
 
     for( i = 0; i < session->trackc; i++ )
-        rtp_del_sink( session->trackv[i].id->sout_id, session->trackv[i].fd );
+        RtspTrackClose( &session->trackv[i] );
 
     free( session->trackv );
     free( session );
 }
 
+/* Look up whether a given session exists for this stream, and if so,
+ * reports the timestamp information used */
+int RtspClientLookup( rtsp_stream_t *rtsp, const char *name, int64_t *ts_init )
+{
+    rtsp_session_t *session;
+
+    vlc_mutex_lock(&rtsp->lock);
+    session = RtspClientGet(rtsp, name);
+    if (session != NULL)
+        *ts_init = session->ts_init;
+    vlc_mutex_unlock(&rtsp->lock);
+
+    return session != NULL ? VLC_SUCCESS : VLC_EGENERIC;
+}
+
+/* Attach a starting VoD RTP id to its RTSP track, and let it
+ * initialize with the parameters of the SETUP request */
+int RtspTrackAttach( rtsp_stream_t *rtsp, const char *name,
+                     rtsp_stream_id_t *id, sout_stream_id_t *sout_id,
+                     uint32_t *ssrc, uint16_t *seq_init )
+{
+    int val = VLC_EGENERIC;
+    rtsp_session_t *session;
+
+    vlc_mutex_lock(&rtsp->lock);
+    session = RtspClientGet(rtsp, name);
+
+    if (session == NULL)
+        goto out;
+
+    for (int i = 0; session->trackc; i++)
+    {
+        rtsp_strack_t *tr = session->trackv + i;
+        if (tr->id == id)
+        {
+            int rtp_fd;
+#if !defined(WIN32) || defined(UNDER_CE)
+            rtp_fd = vlc_dup(tr->setup_fd);
+#else
+            WSAPROTOCOL_INFO info;
+            WSADuplicateSocket (tr->setup_fd, GetCurrentProcessId (), &info);
+            rtp_fd = WSASocket (info.iAddressFamily, info.iSocketType,
+                                info.iProtocol, &info, 0, 0);
+#endif
+            if (rtp_fd == -1)
+                break;
+
+            /* Ignore any unexpected incoming packet */
+            /* XXX: is this needed again? */
+            setsockopt (rtp_fd, SOL_SOCKET, SO_RCVBUF, &(int){ 0 },
+                        sizeof (int));
+
+            uint16_t seq;
+            *ssrc = ntohl(tr->ssrc);
+            *seq_init = tr->seq_init;
+            rtp_add_sink(sout_id, rtp_fd, false, &seq);
+            /* To avoid race conditions, sout_id->i_seq_sent_next must
+             * be set here and now. Make sure the caller did its job
+             * properly when passing seq_init. */
+            assert(tr->seq_init == seq);
+
+            tr->rtp_fd = rtp_fd;
+            tr->sout_id = sout_id;
+            tr->playing = true;
+
+            val = VLC_SUCCESS;
+            break;
+        }
+    }
+
+out:
+    vlc_mutex_unlock(&rtsp->lock);
+    return val;
+}
+
+/* Remove references to the RTP id when it is stopped */
+void RtspTrackDetach( rtsp_stream_t *rtsp, const char *name,
+                      sout_stream_id_t *sout_id )
+{
+    rtsp_session_t *session;
+
+    vlc_mutex_lock(&rtsp->lock);
+    session = RtspClientGet(rtsp, name);
+
+    if (session == NULL)
+        goto out;
+
+    for (int i = 0; session->trackc; i++)
+    {
+        rtsp_strack_t *tr = session->trackv + i;
+        if (tr->sout_id == sout_id)
+        {
+            tr->sout_id = NULL;
+            tr->playing = false;
+            rtp_del_sink(sout_id, tr->rtp_fd);
+            break;
+        }
+    }
+
+out:
+    vlc_mutex_unlock(&rtsp->lock);
+}
+
+
+/** rtsp must be locked */
+static void RtspTrackClose( rtsp_strack_t *tr )
+{
+    if (tr->sout_id != NULL)
+        rtp_del_sink(tr->sout_id, tr->rtp_fd);
+    /* rtp_fd is duplicated from setup_fd only in VoD mode. */
+    if (tr->id->stream->vod_media != NULL)
+        net_Close(tr->setup_fd);
+}
+
 
 /** Finds the next transport choice */
 static inline const char *transport_next( const char *str )
@@ -353,6 +493,28 @@ static inline const char *parameter_next( const char *str )
 }
 
 
+static float ParseNPT (const char *str)
+{
+    locale_t loc = newlocale (LC_NUMERIC_MASK, "C", NULL);
+    locale_t oldloc = uselocale (loc);
+    unsigned hour, min;
+    float sec;
+
+    if (sscanf (str, "%u:%u:%f", &hour, &min, &sec) == 3)
+        sec += ((hour * 60) + min) * 60;
+    else
+    if (sscanf (str, "%f", &sec) != 1)
+        sec = 0.;
+
+    if (loc != (locale_t)0)
+    {
+        uselocale (oldloc);
+        freelocale (loc);
+    }
+    return sec;
+}
+
+
 /** RTSP requests handler
  * @param id selected track for non-aggregate URLs,
  *           NULL for aggregate URLs
@@ -362,11 +524,12 @@ static int RtspHandler( rtsp_stream_t *rtsp, rtsp_stream_id_t *id,
                         httpd_message_t *answer,
                         const httpd_message_t *query )
 {
-    sout_stream_t *p_stream = rtsp->owner;
+    vlc_object_t *p_owner = rtsp->owner;
     char psz_sesbuf[17];
     const char *psz_session = NULL, *psz;
     char control[sizeof("rtsp://[]:12345") + NI_MAXNUMERICHOST
                   + strlen( rtsp->psz_path )];
+    bool vod = rtsp->vod_media != NULL;
     time_t now;
 
     time (&now);
@@ -439,7 +602,10 @@ static int RtspHandler( rtsp_stream_t *rtsp, rtsp_stream_id_t *id,
             answer->i_status = 200;
             httpd_MsgAdd( answer, "Content-Type",  "%s", "application/sdp" );
             httpd_MsgAdd( answer, "Content-Base",  "%s", control );
-            answer->p_body = (uint8_t *)SDPGenerate( rtsp->owner, control );
+
+            answer->p_body = (uint8_t *) ( vod ?
+                SDPGenerateVoD( rtsp->vod_media, control ) :
+                SDPGenerate( (sout_stream_t *)rtsp->owner, control ) );
             if( answer->p_body != NULL )
                 answer->i_body = strlen( (char *)answer->p_body );
             else
@@ -554,8 +720,8 @@ static int RtspHandler( rtsp_stream_t *rtsp, rtsp_stream_id_t *id,
                 {
                     char ip[NI_MAXNUMERICHOST], src[NI_MAXNUMERICHOST];
                     rtsp_session_t *ses = NULL;
-                    rtsp_strack_t track = { id, -1, false };
-                    int sport;
+                    int fd, sport;
+                    uint32_t ssrc;
 
                     if( httpd_ClientIP( cl, ip ) == NULL )
                     {
@@ -563,11 +729,11 @@ static int RtspHandler( rtsp_stream_t *rtsp, rtsp_stream_id_t *id,
                         continue;
                     }
 
-                    track.fd = net_ConnectDgram( p_stream, ip, loport, -1,
-                                                 IPPROTO_UDP );
-                    if( track.fd == -1 )
+                    fd = net_ConnectDgram( p_owner, ip, loport, -1,
+                                           IPPROTO_UDP );
+                    if( fd == -1 )
                     {
-                        msg_Err( p_stream,
+                        msg_Err( p_owner,
                                  "cannot create RTP socket for %s port %u",
                                  ip, loport );
                         answer->i_status = 500;
@@ -575,9 +741,24 @@ static int RtspHandler( rtsp_stream_t *rtsp, rtsp_stream_id_t *id,
                     }
 
                     /* Ignore any unexpected incoming packet */
-                    setsockopt (track.fd, SOL_SOCKET, SO_RCVBUF, &(int){ 0 },
+                    setsockopt (fd, SOL_SOCKET, SO_RCVBUF, &(int){ 0 },
                                 sizeof (int));
-                    net_GetSockAddress( track.fd, src, &sport );
+                    net_GetSockAddress( fd, src, &sport );
+
+                    rtsp_strack_t track = { .id = id, .sout_id = id->sout_id,
+                                            .setup_fd = fd, .playing = false };
+
+                    if (vod)
+                    {
+                        vlc_rand_bytes (&track.seq_init, sizeof (track.seq_init));
+                        vlc_rand_bytes (&track.ssrc, sizeof (track.ssrc));
+                        ssrc = track.ssrc;
+                    }
+                    else
+                    {
+                        track.rtp_fd = track.setup_fd;
+                        ssrc = id->ssrc;
+                    }
 
                     vlc_mutex_lock( &rtsp->lock );
                     if( psz_session == NULL )
@@ -618,7 +799,7 @@ static int RtspHandler( rtsp_stream_t *rtsp, rtsp_stream_id_t *id,
                                       "client_port=%u-%u;server_port=%u-%u;"
                                       "ssrc=%08X;mode=play",
                                       src, loport, loport + 1, sport,
-                                      sport + 1, id->ssrc );
+                                      sport + 1, ssrc );
                     }
                     else
                     {
@@ -627,7 +808,7 @@ static int RtspHandler( rtsp_stream_t *rtsp, rtsp_stream_id_t *id,
                                       "client_port=%u-%u;server_port=%u-%u;"
                                       "ssrc=%08X;mode=play",
                                       loport, loport + 1, sport, sport + 1,
-                                      id->ssrc );
+                                      ssrc );
                     }
 
                     answer->i_status = 200;
@@ -643,7 +824,7 @@ static int RtspHandler( rtsp_stream_t *rtsp, rtsp_stream_id_t *id,
 
             psz_session = httpd_MsgGet( query, "Session" );
             const char *range = httpd_MsgGet (query, "Range");
-            if (range && strncmp (range, "npt=", 4))
+            if (range != NULL && strncmp (range, "npt=", 4))
             {
                 answer->i_status = 501;
                 break;
@@ -660,7 +841,26 @@ static int RtspHandler( rtsp_stream_t *rtsp, rtsp_stream_id_t *id,
                               + sizeof("url=/trackID=123;seq=65535;"
                                        "rtptime=4294967295, ") ) + 1];
                 size_t infolen = 0;
-                int64_t ts = rtp_get_ts( rtsp->owner );
+                int64_t ts;
+                if (vod)
+                {
+                    /* We don't keep a reference to the sout_stream_t,
+                     * so we look for a sout_id to go through. */
+                    ts = ses->ts_init;
+                    for (int i = 0; i < ses->trackc; i++)
+                    {
+                        rtsp_strack_t *tr = ses->trackv + i;
+                        if (tr->sout_id != NULL)
+                        {
+                            sout_stream_t *p_stream =
+                                                rtp_get_pstream(tr->sout_id);
+                            ts = rtp_get_ts(p_stream);
+                            break;
+                        }
+                    }
+                }
+                else
+                    ts = rtp_get_ts( (sout_stream_t *)rtsp->owner );
 
                 for( int i = 0; i < ses->trackc; i++ )
                 {
@@ -670,17 +870,29 @@ static int RtspHandler( rtsp_stream_t *rtsp, rtsp_stream_id_t *id,
                         uint16_t seq;
                         if( !tr->playing )
                         {
-                            tr->playing = true;
-                            rtp_add_sink( tr->id->sout_id, tr->fd, false,
-                                          &seq );
+                            if (vod)
+                                /* TODO: if the RTP stream output is already
+                                 * started, it won't pick up newly set-up
+                                 * tracks, so we need to call rtp_add_sink()
+                                 * or something. */
+                                seq = tr->seq_init;
+                            else
+                            {
+                                tr->playing = true;
+                                rtp_add_sink( tr->sout_id, tr->rtp_fd,
+                                              false, &seq );
+                            }
                         }
                         else
-                            seq = rtp_get_seq( tr->id->sout_id );
+                        {
+                            assert( tr->sout_id != NULL );
+                            seq = rtp_get_seq( tr->sout_id );
+                        }
                         char *url = RtspAppendTrackPath( tr->id, control );
                         infolen += sprintf( info + infolen,
                                     "url=%s;seq=%u;rtptime=%u, ",
                                     url != NULL ? url : "", seq,
-                                    rtp_compute_ts( tr->id->sout_id, ts ) );
+                                    rtp_compute_ts( tr->id->clock_rate, ts ) );
                         free( url );
                     }
                 }
@@ -689,6 +901,31 @@ static int RtspHandler( rtsp_stream_t *rtsp, rtsp_stream_id_t *id,
                     info[infolen - 2] = '\0'; /* remove trailing ", " */
                     httpd_MsgAdd( answer, "RTP-Info", "%s", info );
                 }
+                if (vod)
+                {
+                    /* TODO: fix that crap, this is barely RTSP */
+                    if (!ses->vod_started)
+                    {
+                        if (vod_start(rtsp->vod_media, psz_session)
+                                != VLC_SUCCESS)
+                            answer->i_status = 500;
+                        else
+                            ses->vod_started = true;
+                    }
+                    else
+                    {
+                        if (range != NULL)
+                        {
+                            float time = ParseNPT (range + 4);
+                            vod_seek(rtsp->vod_media, psz_session, time);
+                        }
+                        if (ses->paused)
+                        {
+                            vod_toggle_pause(rtsp->vod_media, psz_session);
+                            ses->paused = false;
+                        }
+                    }
+                }
             }
             vlc_mutex_unlock( &rtsp->lock );
 
@@ -698,11 +935,29 @@ static int RtspHandler( rtsp_stream_t *rtsp, rtsp_stream_id_t *id,
         }
 
         case HTTPD_MSG_PAUSE:
-            answer->i_status = 405;
-            httpd_MsgAdd( answer, "Allow",
-                          "%s, TEARDOWN, PLAY, GET_PARAMETER",
-                          ( id != NULL ) ? "SETUP" : "DESCRIBE" );
+        {
+            if (!vod)
+            {
+                answer->i_status = 405;
+                httpd_MsgAdd( answer, "Allow",
+                              "%s, TEARDOWN, PLAY, GET_PARAMETER",
+                              ( id != NULL ) ? "SETUP" : "DESCRIBE" );
+                break;
+            }
+
+            rtsp_session_t *ses;
+            answer->i_status = 200;
+            psz_session = httpd_MsgGet( query, "Session" );
+            vlc_mutex_lock( &rtsp->lock );
+            ses = RtspClientGet( rtsp, psz_session );
+            if (ses != NULL && !ses->paused)
+            {
+                vod_toggle_pause(rtsp->vod_media, psz_session);
+                ses->paused = true;
+            }
+            vlc_mutex_unlock( &rtsp->lock );
             break;
+        }
 
         case HTTPD_MSG_GETPARAMETER:
             if( query->i_body > 0 )
@@ -728,13 +983,17 @@ static int RtspHandler( rtsp_stream_t *rtsp, rtsp_stream_id_t *id,
             if( ses != NULL )
             {
                 if( id == NULL ) /* Delete the entire session */
+                {
                     RtspClientDel( rtsp, ses );
+                    if (vod)
+                        vod_stop(rtsp->vod_media, psz_session);
+                }
                 else /* Delete one track from the session */
                 for( int i = 0; i < ses->trackc; i++ )
                 {
                     if( ses->trackv[i].id == id )
                     {
-                        rtp_del_sink( id->sout_id, ses->trackv[i].fd );
+                        RtspTrackClose( &ses->trackv[i] );
                         REMOVE_ELEM( ses->trackv, ses->trackc, i );
                     }
                 }
diff --git a/modules/stream_out/vod.c b/modules/stream_out/vod.c
new file mode 100644
index 0000000..35b757b
--- /dev/null
+++ b/modules/stream_out/vod.c
@@ -0,0 +1,719 @@
+/*****************************************************************************
+ * vod.c: rtsp VoD server module
+ *****************************************************************************
+ * Copyright (C) 2003-2006, 2010 the VideoLAN team
+ * $Id$
+ *
+ * Authors: Laurent Aimar <fenrir at via.ecp.fr>
+ *          Gildas Bazin <gbazin at videolan.org>
+ *          Pierre Ynard
+ *
+ * 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_input.h>
+#include <vlc_sout.h>
+#include <vlc_block.h>
+
+#include <vlc_vod.h>
+#include <vlc_url.h>
+#include <vlc_network.h>
+
+#include <assert.h>
+
+#include "rtp.h"
+
+/*****************************************************************************
+ * Exported prototypes
+ *****************************************************************************/
+
+typedef struct media_es_t media_es_t;
+
+struct media_es_t
+{
+    int es_id;
+    rtp_format_t rtp_fmt;
+    rtsp_stream_id_t *rtsp_id;
+};
+
+struct vod_media_t
+{
+    int id;
+
+    /* VoD server */
+    vod_t *p_vod;
+
+    /* RTSP server */
+    rtsp_stream_t *rtsp;
+
+    /* ES list */
+    int        i_es;
+    media_es_t **es;
+    const char *psz_mux;
+
+    /* Infos */
+    mtime_t i_length;
+};
+
+struct vod_sys_t
+{
+    char *psz_rtsp_url;
+
+    /* List of media */
+    vlc_mutex_t lock_media;
+    int i_media_id;
+    int i_media;
+    vod_media_t **media;
+
+    /* */
+    block_fifo_t *p_fifo_cmd;
+};
+
+/* rtsp delayed command (to avoid deadlock between vlm/httpd) */
+typedef enum
+{
+    RTSP_CMD_TYPE_NONE,  /* Exit requested */
+
+    RTSP_CMD_TYPE_PLAY,
+    RTSP_CMD_TYPE_PAUSE,
+    RTSP_CMD_TYPE_STOP,
+    RTSP_CMD_TYPE_SEEK,
+#if 0
+    RTSP_CMD_TYPE_REWIND,
+    RTSP_CMD_TYPE_FORWARD,
+#endif
+
+    RTSP_CMD_TYPE_ADD,
+    RTSP_CMD_TYPE_DEL,
+} rtsp_cmd_type_t;
+
+/* */
+typedef struct
+{
+    int i_type;
+    int i_media_id;
+    vod_media_t *p_media;
+    char *psz_session;
+    char *psz_arg;
+    double f_arg;
+} rtsp_cmd_t;
+
+static vod_media_t *MediaNew( vod_t *, const char *, input_item_t * );
+static void         MediaDel( vod_t *, vod_media_t * );
+static void         MediaAskDel ( vod_t *, vod_media_t * );
+
+static void* CommandThread( vlc_object_t *p_this );
+static void  CommandPush( vod_t *, rtsp_cmd_type_t, vod_media_t *, const char *psz_session,
+                          double f_arg, const char *psz_arg );
+
+/*****************************************************************************
+ * Open: Starts the RTSP server module
+ *****************************************************************************/
+int OpenVoD( vlc_object_t *p_this )
+{
+    vod_t *p_vod = (vod_t *)p_this;
+    vod_sys_t *p_sys = NULL;
+    char *psz_url;
+
+    p_vod->p_sys = p_sys = malloc( sizeof( vod_sys_t ) );
+    if( !p_sys ) goto error;
+
+    psz_url = var_InheritString( p_vod, "rtsp-host" );
+
+    if( psz_url == NULL )
+        p_sys->psz_rtsp_url = strdup( "/" );
+    else
+    if( !( strlen( psz_url ) > 0 && psz_url[strlen( psz_url ) - 1] == '/' ) )
+    {
+         if( asprintf( &p_sys->psz_rtsp_url, "%s/", psz_url ) == -1 )
+         {
+             p_sys->psz_rtsp_url = NULL;
+             free( psz_url );
+             goto error;
+         }
+         free( psz_url );
+    }
+    else
+        p_sys->psz_rtsp_url = psz_url;
+
+    vlc_mutex_init( &p_sys->lock_media );
+
+    TAB_INIT( p_sys->i_media, p_sys->media );
+    p_sys->i_media_id = 0;
+
+    p_vod->pf_media_new = MediaNew;
+    p_vod->pf_media_del = MediaAskDel;
+    /* These are never used in the core, wonder why they're in the API */
+    p_vod->pf_media_add_es = NULL;
+    p_vod->pf_media_del_es = NULL;
+
+    p_sys->p_fifo_cmd = block_FifoNew();
+    if( vlc_thread_create( p_vod, "rtsp vod thread", CommandThread,
+                           VLC_THREAD_PRIORITY_LOW ) )
+    {
+        msg_Err( p_vod, "cannot spawn rtsp vod thread" );
+        block_FifoRelease( p_sys->p_fifo_cmd );
+        goto error;
+    }
+
+    return VLC_SUCCESS;
+
+error:
+    if( p_sys )
+    {
+        free( p_sys->psz_rtsp_url );
+        free( p_sys );
+    }
+
+    return VLC_EGENERIC;
+}
+
+/*****************************************************************************
+ * Close:
+ *****************************************************************************/
+void CloseVoD( vlc_object_t * p_this )
+{
+    vod_t *p_vod = (vod_t *)p_this;
+    vod_sys_t *p_sys = p_vod->p_sys;
+
+    /* Stop command thread */
+    vlc_object_kill( p_vod );
+    CommandPush( p_vod, RTSP_CMD_TYPE_NONE, NULL, NULL, 0.0, NULL );
+    vlc_thread_join( p_vod );
+
+    while( block_FifoCount( p_sys->p_fifo_cmd ) > 0 )
+    {
+        rtsp_cmd_t cmd;
+        block_t *p_block_cmd = block_FifoGet( p_sys->p_fifo_cmd );
+        memcpy( &cmd, p_block_cmd->p_buffer, sizeof(cmd) );
+        block_Release( p_block_cmd );
+        if ( cmd.i_type == RTSP_CMD_TYPE_DEL )
+            MediaDel(p_vod, cmd.p_media);
+        free( cmd.psz_session );
+        free( cmd.psz_arg );
+    }
+    block_FifoRelease( p_sys->p_fifo_cmd );
+
+    /* Check VLM is not buggy */
+    if( p_sys->i_media > 0 )
+        msg_Err( p_vod, "rtsp vod leaking %d medias", p_sys->i_media );
+    TAB_CLEAN( p_sys->i_media, p_sys->media );
+
+    vlc_mutex_destroy( &p_sys->lock_media );
+
+    free( p_sys->psz_rtsp_url );
+    free( p_sys );
+}
+
+/*****************************************************************************
+ * Media handling
+ *****************************************************************************/
+static vod_media_t *MediaNew( vod_t *p_vod, const char *psz_name,
+                              input_item_t *p_item )
+{
+    vod_sys_t *p_sys = p_vod->p_sys;
+
+    vod_media_t *p_media = calloc( 1, sizeof(vod_media_t) );
+    if( !p_media )
+        return NULL;
+
+    p_media->p_vod = p_vod;
+    p_media->rtsp = NULL;
+    TAB_INIT( p_media->i_es, p_media->es );
+    p_media->psz_mux = NULL;
+    p_media->i_length = input_item_GetDuration( p_item );
+
+    int i_payload_type = 96;
+
+    vlc_mutex_lock( &p_item->lock );
+    msg_Dbg( p_vod, "media '%s' has %i declared ES", psz_name, p_item->i_es );
+    for( int i = 0; i < p_item->i_es; i++ )
+    {
+        es_format_t *p_fmt = p_item->es[i];
+
+        switch( p_fmt->i_codec )
+        {
+            case VLC_FOURCC( 'm', 'p', '2', 't' ):
+                p_media->psz_mux = "ts";
+                break;
+            case VLC_FOURCC( 'm', 'p', '2', 'p' ):
+                p_media->psz_mux = "ps";
+                break;
+        }
+        assert(p_media->psz_mux == NULL || p_item->i_es == 1);
+
+        media_es_t *p_es = calloc( 1, sizeof(media_es_t) );
+        if( !p_es )
+            continue;
+
+        p_es->es_id = p_fmt->i_id;
+        p_es->rtsp_id = NULL;
+
+        rtp_get_fmt(VLC_OBJECT(p_vod), p_fmt, p_media->psz_mux,
+                    &p_es->rtp_fmt);
+
+        if (p_es->rtp_fmt.ptname == NULL)
+        {
+            free(p_es);
+            continue;
+        }
+
+        if (p_es->rtp_fmt.payload_type >= 96)
+        {
+            if (i_payload_type >= 128)
+            {
+                msg_Err(p_vod, "cannot add ES %s (%4.4s): no dynamic "
+                               "payload left", p_es->rtp_fmt.ptname,
+                        (char *)&p_fmt->i_codec);
+                free(p_es->rtp_fmt.fmtp);
+                free(p_es);
+                continue;
+            }
+            p_es->rtp_fmt.payload_type = i_payload_type++;
+        }
+
+        TAB_APPEND( p_media->i_es, p_media->es, p_es );
+        msg_Dbg(p_vod, "  - added ES %u %s (%4.4s)",
+                p_es->rtp_fmt.payload_type, p_es->rtp_fmt.ptname,
+                (char *)&p_fmt->i_codec);
+    }
+    vlc_mutex_unlock( &p_item->lock );
+
+    if (p_media->i_es == 0)
+    {
+        msg_Err(p_vod, "no ES was added to the media, aborting");
+        goto error;
+    }
+
+    char *psz_url;
+
+    if( asprintf( &psz_url, "%s%s", p_sys->psz_rtsp_url, psz_name ) < 0 )
+        goto error;
+
+    vlc_url_t url;
+    vlc_UrlParse( &url, psz_url, 0 );
+    free( psz_url );
+
+    p_media->rtsp = RtspSetup(VLC_OBJECT(p_vod), p_media, &url);
+
+    vlc_UrlClean( &url );
+
+    if (p_media->rtsp == NULL)
+        goto error;
+
+    for (int i = 0; i < p_media->i_es; i++)
+    {
+        media_es_t *p_es = p_media->es[i];
+        p_es->rtsp_id = RtspAddId(p_media->rtsp, NULL, 0,
+                                  p_es->rtp_fmt.clock_rate, NULL, 0, 0, 0);
+        if (p_es->rtsp_id == NULL)
+            goto error;
+    }
+
+    p_media->id = p_sys->i_media_id++;
+
+    msg_Dbg(p_vod, "adding media '%s', id %i", psz_name, p_media->id);
+
+    CommandPush( p_vod, RTSP_CMD_TYPE_ADD, p_media, NULL, 0.0, NULL );
+    return p_media;
+
+error:
+    MediaDel(p_vod, p_media);
+    return NULL;
+}
+
+static void MediaAskDel ( vod_t *p_vod, vod_media_t *p_media )
+{
+    msg_Dbg( p_vod, "deleting media id %i", p_media->id );
+    CommandPush( p_vod, RTSP_CMD_TYPE_DEL, p_media, NULL, 0.0, NULL );
+}
+
+static void MediaDel( vod_t *p_vod, vod_media_t *p_media )
+{
+    vod_sys_t *p_sys = p_vod->p_sys;
+
+    vlc_mutex_lock( &p_sys->lock_media );
+    TAB_REMOVE( p_sys->i_media, p_sys->media, p_media );
+    vlc_mutex_unlock( &p_sys->lock_media );
+
+    if (p_media->rtsp != NULL)
+    {
+        for (int i = 0; i < p_media->i_es; i++)
+        {
+            media_es_t *p_es = p_media->es[i];
+            if (p_es->rtsp_id != NULL)
+                RtspDelId(p_media->rtsp, p_es->rtsp_id);
+        }
+        RtspUnsetup(p_media->rtsp);
+    }
+
+    while( p_media->i_es )
+    {
+        media_es_t *p_es = p_media->es[0];
+        TAB_REMOVE( p_media->i_es, p_media->es, p_es );
+        free( p_es->rtp_fmt.fmtp );
+        free( p_es );
+    }
+
+    TAB_CLEAN( p_media->i_es, p_media->es );
+    free( p_media );
+}
+
+static void CommandPush( vod_t *p_vod, rtsp_cmd_type_t i_type, vod_media_t *p_media, const char *psz_session,
+                         double f_arg, const char *psz_arg )
+{
+    rtsp_cmd_t cmd;
+    block_t *p_cmd;
+
+    memset( &cmd, 0, sizeof(cmd) );
+    cmd.i_type = i_type;
+    cmd.p_media = p_media;
+    if( p_media )
+        cmd.i_media_id = p_media->id;
+    if( psz_session )
+        cmd.psz_session = strdup(psz_session);
+    cmd.f_arg = f_arg;
+    if( psz_arg )
+        cmd.psz_arg = strdup(psz_arg);
+
+    p_cmd = block_New( p_vod, sizeof(rtsp_cmd_t) );
+    memcpy( p_cmd->p_buffer, &cmd, sizeof(cmd) );
+
+    block_FifoPut( p_vod->p_sys->p_fifo_cmd, p_cmd );
+}
+
+static void* CommandThread( vlc_object_t *p_this )
+{
+    vod_t *p_vod = (vod_t*)p_this;
+    vod_sys_t *p_sys = p_vod->p_sys;
+    int canc = vlc_savecancel ();
+
+    while( vlc_object_alive (p_vod) )
+    {
+        block_t *p_block_cmd = block_FifoGet( p_sys->p_fifo_cmd );
+        rtsp_cmd_t cmd;
+        vod_media_t *p_media = NULL;
+        int i;
+
+        if( !p_block_cmd )
+            break;
+
+        memcpy( &cmd, p_block_cmd->p_buffer, sizeof(cmd) );
+        block_Release( p_block_cmd );
+
+        if( cmd.i_type == RTSP_CMD_TYPE_NONE )
+            break;
+
+        if ( cmd.i_type == RTSP_CMD_TYPE_ADD )
+        {
+            vlc_mutex_lock( &p_sys->lock_media );
+            TAB_APPEND( p_sys->i_media, p_sys->media, cmd.p_media );
+            vlc_mutex_unlock( &p_sys->lock_media );
+            goto next;
+        }
+
+        if ( cmd.i_type == RTSP_CMD_TYPE_DEL )
+        {
+            MediaDel(p_vod, cmd.p_media);
+            goto next;
+        }
+
+        /* */
+        vlc_mutex_lock( &p_sys->lock_media );
+        for( i = 0; i < p_sys->i_media; i++ )
+        {
+            if( p_sys->media[i]->id == cmd.i_media_id )
+                break;
+        }
+        if( i >= p_sys->i_media )
+        {
+            vlc_mutex_unlock( &p_sys->lock_media );
+            goto next;
+        }
+        p_media = p_sys->media[i];
+
+        switch( cmd.i_type )
+        {
+        case RTSP_CMD_TYPE_PLAY:
+            vod_MediaControl( p_vod, p_media, cmd.psz_session,
+                              VOD_MEDIA_PLAY, cmd.psz_arg );
+            break;
+        case RTSP_CMD_TYPE_PAUSE:
+            vod_MediaControl( p_vod, p_media, cmd.psz_session,
+                              VOD_MEDIA_PAUSE );
+            break;
+
+        case RTSP_CMD_TYPE_STOP:
+            vod_MediaControl( p_vod, p_media, cmd.psz_session, VOD_MEDIA_STOP );
+            break;
+
+        case RTSP_CMD_TYPE_SEEK:
+            vod_MediaControl( p_vod, p_media, cmd.psz_session,
+                              VOD_MEDIA_SEEK, cmd.f_arg );
+            break;
+
+#if 0
+        case RTSP_CMD_TYPE_REWIND:
+            vod_MediaControl( p_vod, p_media, cmd.psz_session,
+                              VOD_MEDIA_REWIND, cmd.f_arg );
+            break;
+
+        case RTSP_CMD_TYPE_FORWARD:
+            vod_MediaControl( p_vod, p_media, cmd.psz_session,
+                              VOD_MEDIA_FORWARD, cmd.f_arg );
+            break;
+#endif
+
+        default:
+            break;
+        }
+        vlc_mutex_unlock( &p_sys->lock_media );
+
+    next:
+        free( cmd.psz_session );
+        free( cmd.psz_arg );
+    }
+
+    vlc_restorecancel (canc);
+    return NULL;
+}
+
+/*****************************************************************************
+ * SDPGenerateVoD
+ * FIXME: needs to be merged more?
+ *****************************************************************************/
+char *SDPGenerateVoD( const vod_media_t *p_media, const char *rtsp_url )
+{
+    char *psz_sdp;
+
+    assert(rtsp_url != NULL);
+    /* Check against URL format rtsp://[<ipv6>]:<port>/<path> */
+    bool ipv6 = strlen( rtsp_url ) > 7 && rtsp_url[7] == '[';
+
+    /* Dummy destination address for RTSP */
+    struct sockaddr_storage dst;
+    socklen_t dstlen = ipv6 ? sizeof( struct sockaddr_in6 )
+                            : sizeof( struct sockaddr_in );
+    memset (&dst, 0, dstlen);
+    dst.ss_family = ipv6 ? AF_INET6 : AF_INET;
+#ifdef HAVE_SA_LEN
+    dst.ss_len = dstlen;
+#endif
+
+    psz_sdp = vlc_sdp_Start( VLC_OBJECT( p_media->p_vod ), "sout-rtp-",
+                             NULL, 0, (struct sockaddr *)&dst, dstlen );
+    if( psz_sdp == NULL )
+        return NULL;
+
+    if( p_media->i_length > 0 )
+    {
+        lldiv_t d = lldiv( p_media->i_length / 1000, 1000 );
+        sdp_AddAttribute( &psz_sdp, "range"," npt=0-%lld.%03u", d.quot,
+                          (unsigned)d.rem );
+    }
+
+    sdp_AddAttribute ( &psz_sdp, "control", "%s", rtsp_url );
+
+    /* No locking needed, the ES table can't be modified now */
+    for( int i = 0; i < p_media->i_es; i++ )
+    {
+        media_es_t *p_es = p_media->es[i];
+        rtp_format_t *rtp_fmt = &p_es->rtp_fmt;
+        const char *mime_major; /* major MIME type */
+
+        switch( rtp_fmt->cat )
+        {
+            case VIDEO_ES:
+                mime_major = "video";
+                break;
+            case AUDIO_ES:
+                mime_major = "audio";
+                break;
+            case SPU_ES:
+                mime_major = "text";
+                break;
+            default:
+                continue;
+        }
+
+        sdp_AddMedia( &psz_sdp, mime_major, "RTP/AVP", 0,
+                      rtp_fmt->payload_type, false, 0,
+                      rtp_fmt->ptname, rtp_fmt->clock_rate, rtp_fmt->channels,
+                      rtp_fmt->fmtp );
+
+        char *track_url = RtspAppendTrackPath( p_es->rtsp_id, rtsp_url );
+        if( track_url != NULL )
+        {
+            sdp_AddAttribute ( &psz_sdp, "control", "%s", track_url );
+            free( track_url );
+        }
+    }
+
+    return psz_sdp;
+}
+
+int vod_start(vod_media_t *p_media, const char *psz_session)
+{
+    char *psz_output;
+    if (asprintf(&psz_output, "rtp{vod-session=%s}", psz_session) == -1)
+        return VLC_ENOMEM;
+
+    CommandPush(p_media->p_vod, RTSP_CMD_TYPE_PLAY, p_media,
+                psz_session, 0.0, psz_output);
+
+    free(psz_output);
+    return VLC_SUCCESS;
+}
+
+/* FIXME: this sucks, RTSP doesn't really toggle the pause state, it
+ * either PAUSEs or PLAYs */
+void vod_toggle_pause(vod_media_t *p_media, const char *psz_session)
+{
+    CommandPush(p_media->p_vod, RTSP_CMD_TYPE_PAUSE, p_media,
+                psz_session, 0.0, NULL);
+}
+
+void vod_stop(vod_media_t *p_media, const char *psz_session)
+{
+    CommandPush(p_media->p_vod, RTSP_CMD_TYPE_STOP, p_media,
+                psz_session, 0.0, NULL);
+}
+
+void vod_seek(vod_media_t *p_media, const char *psz_session, float time)
+{
+    /* FIXME: why do we even bother converting from time to position??? */
+    double position = time / (((double)(p_media->i_length)) / CLOCK_FREQ / 100);
+    CommandPush(p_media->p_vod, RTSP_CMD_TYPE_SEEK, p_media,
+                psz_session, position, NULL);
+}
+
+/* XXX: eww gross */
+struct vlm_t
+{
+    VLC_COMMON_MEMBERS
+
+    vlc_mutex_t  lock;
+    vlc_thread_t thread;
+    vlc_mutex_t  lock_manage;
+    vlc_cond_t   wait_manage;
+    /* tell vlm thread there is work to do */
+    bool         input_state_changed;
+    /* */
+    int64_t        i_id;
+
+    /* Vod server (used by media) */
+    vod_t          *p_vod;
+
+    /* Skip the rest... */
+};
+
+/* Walk up the object tree to fetch the VoD object
+ * FIXME: do this properly... or even not at all */
+static vod_t *vod_get_vod_object(sout_stream_t *p_stream)
+{
+#if 0
+    vlc_object_t *p_input = vlc_object_find(p_stream, VLC_OBJECT_INPUT,
+                                            FIND_PARENT);
+    vlc_object_release(p_input);
+    return (vod_t *) p_input->p_parent;
+#endif
+    vlm_t *p_vlm = (vlm_t *) p_stream->p_parent->p_parent;
+    return p_vlm->p_vod;
+}
+
+/* Allow the RTP stream output module to fetch data from their
+ * corresponding VoD media and RTSP session established by the VoD code */
+vod_media_t *vod_get_session(sout_stream_t *p_stream, const char *psz_session,
+                             const char **mux, int64_t *i_ts_init)
+{
+    vod_t *p_vod = vod_get_vod_object(p_stream);
+    vod_sys_t *p_sys = p_vod->p_sys;
+    vod_media_t *p_media = NULL;
+
+    vlc_mutex_lock(&p_sys->lock_media);
+    for (int i = 0; i < p_sys->i_media; i++)
+    {
+        if (RtspClientLookup(p_sys->media[i]->rtsp, psz_session,
+                             i_ts_init) == VLC_SUCCESS)
+        {
+            p_media = p_sys->media[i];
+            *mux = p_media->psz_mux;
+            break;
+        }
+    }
+    vlc_mutex_unlock(&p_sys->lock_media);
+
+    /* The VLM stops all instances before deleting a media, so this
+     * reference will remain valid during the lifetime of the rtp
+     * stream output using it. */
+    return p_media;
+}
+
+/* Match an RTP id to a VoD media ES and RTSP track to initialize it
+ * with the data that was already set up */
+int vod_init_id(vod_media_t *p_media, const char *psz_session, int es_id,
+                sout_stream_id_t *sout_id, rtp_format_t *rtp_fmt,
+                uint32_t *ssrc, uint16_t *seq_init)
+{
+    media_es_t *p_es;
+
+    if (p_media->psz_mux != NULL)
+    {
+        assert(p_media->i_es == 1);
+        p_es = p_media->es[0];
+    }
+    else
+    {
+        p_es = NULL;
+        /* No locking needed, the ES table can't be modified now */
+        for (int i = 0; i < p_media->i_es; i++)
+        {
+            if (p_media->es[i]->es_id == es_id)
+            {
+                p_es = p_media->es[i];
+                break;
+            }
+        }
+        if (p_es == NULL)
+            return VLC_EGENERIC;
+    }
+
+    memcpy(rtp_fmt, &p_es->rtp_fmt, sizeof(*rtp_fmt));
+    if (p_es->rtp_fmt.fmtp != NULL)
+        rtp_fmt->fmtp = strdup(p_es->rtp_fmt.fmtp);
+    if (RtspTrackAttach(p_media->rtsp, psz_session, p_es->rtsp_id,
+                        sout_id, ssrc, seq_init) != VLC_SUCCESS)
+        return VLC_EGENERIC;
+
+    return VLC_SUCCESS;
+}
+
+/* Remove references to the RTP id from its RTSP track */
+void vod_detach_id(vod_media_t *p_media, const char *psz_session,
+                   sout_stream_id_t *sout_id)
+{
+    RtspTrackDetach(p_media->rtsp, psz_session, sout_id);
+}
+


Regards,

-- 
Pierre Ynard
"Une âme dans un corps, c'est comme un dessin sur une feuille de papier."



More information about the vlc-devel mailing list