rtsp.c
上传用户:kjfoods
上传日期:2020-07-06
资源大小:29949k
文件大小:55k
源码类别:

midi

开发平台:

Unix_Linux

  1. /*****************************************************************************
  2.  * rtsp.c: rtsp VoD server module
  3.  *****************************************************************************
  4.  * Copyright (C) 2003-2006 the VideoLAN team
  5.  * $Id: 34ed0586d7e22ae2932f582068fa5cab75ca4482 $
  6.  *
  7.  * Authors: Laurent Aimar <fenrir@via.ecp.fr>
  8.  *          Gildas Bazin <gbazin@videolan.org>
  9.  *
  10.  * This program is free software; you can redistribute it and/or modify
  11.  * it under the terms of the GNU General Public License as published by
  12.  * the Free Software Foundation; either version 2 of the License, or
  13.  * (at your option) any later version.
  14.  *
  15.  * This program is distributed in the hope that it will be useful,
  16.  * but WITHOUT ANY WARRANTY; without even the implied warranty of
  17.  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  18.  * GNU General Public License for more details.
  19.  *
  20.  * You should have received a copy of the GNU General Public License
  21.  * along with this program; if not, write to the Free Software
  22.  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
  23.  *****************************************************************************/
  24. /*****************************************************************************
  25.  * Preamble
  26.  *****************************************************************************/
  27. #ifdef HAVE_CONFIG_H
  28. # include "config.h"
  29. #endif
  30. #include <vlc_common.h>
  31. #include <vlc_plugin.h>
  32. #include <vlc_input.h>
  33. #include <vlc_sout.h>
  34. #include <vlc_block.h>
  35. #include "vlc_httpd.h"
  36. #include "vlc_vod.h"
  37. #include "vlc_url.h"
  38. #include <vlc_network.h>
  39. #include <vlc_charset.h>
  40. #include <vlc_strings.h>
  41. #include <errno.h>
  42. #ifndef WIN32
  43. # include <locale.h>
  44. #endif
  45. #ifdef HAVE_XLOCALE_H
  46. # include <xlocale.h>
  47. #endif
  48. /*****************************************************************************
  49.  * Module descriptor
  50.  *****************************************************************************/
  51. static int  Open ( vlc_object_t * );
  52. static void Close( vlc_object_t * );
  53. #define HOST_TEXT N_( "RTSP host address" )
  54. #define HOST_LONGTEXT N_( 
  55.     "This defines the address, port and path the RTSP VOD server will listen " 
  56.     "on.nSyntax is address:port/path. The default is to listen on all "
  57.     "interfaces (address 0.0.0.0), on port 554, with no path.nTo listen " 
  58.     "only on the local interface, use "localhost" as address." )
  59. #define THROTLE_TEXT N_( "Maximum number of connections" )
  60. #define THROTLE_LONGTEXT N_( "This limits the maximum number of clients " 
  61.     "that can connect to the RTSP VOD. 0 means no limit."  )
  62. #define RAWMUX_TEXT N_( "MUX for RAW RTSP transport" )
  63. #define SESSION_TIMEOUT_TEXT N_( "Sets the timeout option in the RTSP " 
  64.     "session string" )
  65. #define SESSION_TIMEOUT_LONGTEXT N_( "Defines what timeout option to add " 
  66.     "to the RTSP session ID string. Setting it to a negative number removes " 
  67.     "the timeout option entirely. This is needed by some IPTV STBs (such as " 
  68.     "those made by HansunTech) which get confused by it. The default is 5." )
  69. vlc_module_begin ()
  70.     set_shortname( N_("RTSP VoD" ) )
  71.     set_description( N_("RTSP VoD server") )
  72.     set_category( CAT_SOUT )
  73.     set_subcategory( SUBCAT_SOUT_VOD )
  74.     set_capability( "vod server", 1 )
  75.     set_callbacks( Open, Close )
  76.     add_shortcut( "rtsp" )
  77.     add_string ( "rtsp-host", NULL, NULL, HOST_TEXT, HOST_LONGTEXT, true )
  78.     add_string( "rtsp-raw-mux", "ts", NULL, RAWMUX_TEXT,
  79.                 RAWMUX_TEXT, true )
  80.     add_integer( "rtsp-throttle-users", 0, NULL, THROTLE_TEXT,
  81.                                            THROTLE_LONGTEXT, true )
  82.     add_integer( "rtsp-session-timeout", 5, NULL, SESSION_TIMEOUT_TEXT,
  83.                  SESSION_TIMEOUT_LONGTEXT, true )
  84. vlc_module_end ()
  85. /*****************************************************************************
  86.  * Exported prototypes
  87.  *****************************************************************************/
  88. typedef struct media_es_t media_es_t;
  89. typedef struct
  90. {
  91.     media_es_t *p_media_es;
  92.     char *psz_ip;
  93.     int i_port;
  94. } rtsp_client_es_t;
  95. typedef struct
  96. {
  97.     char *psz_session;
  98.     int64_t i_last; /* for timeout */
  99.     bool b_playing; /* is it in "play" state */
  100.     bool b_paused; /* is it in "pause" state */
  101.     int i_es;
  102.     rtsp_client_es_t **es;
  103. } rtsp_client_t;
  104. struct media_es_t
  105. {
  106.     /* VoD server */
  107.     vod_t *p_vod;
  108.     /* RTSP server */
  109.     httpd_url_t *p_rtsp_url;
  110.     vod_media_t *p_media;
  111.     es_format_t fmt;
  112.     int         i_port;
  113.     uint8_t     i_payload_type;
  114.     char        *psz_rtpmap;
  115.     char        *psz_fmtp;
  116. };
  117. struct vod_media_t
  118. {
  119.     int id;
  120.     /* VoD server */
  121.     vod_t *p_vod;
  122.     /* RTSP server */
  123.     httpd_url_t  *p_rtsp_url;
  124.     char         *psz_rtsp_control_v4;
  125.     char         *psz_rtsp_control_v6;
  126.     char         *psz_rtsp_path;
  127.     int  i_port;
  128.     int  i_port_audio;
  129.     int  i_port_video;
  130.     int  i_ttl;
  131.     int  i_payload_type;
  132.     int64_t i_sdp_id;
  133.     int     i_sdp_version;
  134.     bool b_multicast;
  135.     vlc_mutex_t lock;
  136.     /* ES list */
  137.     int        i_es;
  138.     media_es_t **es;
  139.     char       *psz_mux;
  140.     bool  b_raw;
  141.     /* RTSP client */
  142.     int           i_rtsp;
  143.     rtsp_client_t **rtsp;
  144.     /* Infos */
  145.     char *psz_session_name;
  146.     char *psz_session_description;
  147.     char *psz_session_url;
  148.     char *psz_session_email;
  149.     mtime_t i_length;
  150. };
  151. struct vod_sys_t
  152. {
  153.     /* RTSP server */
  154.     httpd_host_t *p_rtsp_host;
  155.     char *psz_path;
  156.     int i_port;
  157.     int i_throttle_users;
  158.     int i_connections;
  159.     char *psz_raw_mux;
  160.     int i_session_timeout;
  161.     /* List of media */
  162.     vlc_mutex_t lock_media;
  163.     int i_media_id;
  164.     int i_media;
  165.     vod_media_t **media;
  166.     /* */
  167.     block_fifo_t *p_fifo_cmd;
  168. };
  169. /* rtsp delayed command (to avoid deadlock between vlm/httpd) */
  170. typedef enum
  171. {
  172.     RTSP_CMD_TYPE_NONE,  /* Exit requested */
  173.     RTSP_CMD_TYPE_PLAY,
  174.     RTSP_CMD_TYPE_PAUSE,
  175.     RTSP_CMD_TYPE_STOP,
  176.     RTSP_CMD_TYPE_SEEK,
  177.     RTSP_CMD_TYPE_REWIND,
  178.     RTSP_CMD_TYPE_FORWARD,
  179.     RTSP_CMD_TYPE_ADD,
  180.     RTSP_CMD_TYPE_DEL,
  181. } rtsp_cmd_type_t;
  182. /* */
  183. typedef struct
  184. {
  185.     int i_type;
  186.     int i_media_id;
  187.     vod_media_t *p_media;
  188.     char *psz_session;
  189.     char *psz_arg;
  190.     double f_arg;
  191. } rtsp_cmd_t;
  192. static vod_media_t *MediaNew( vod_t *, const char *, input_item_t * );
  193. static void         MediaDel( vod_t *, vod_media_t * );
  194. static void         MediaAskDel ( vod_t *, vod_media_t * );
  195. static int          MediaAddES( vod_t *, vod_media_t *, es_format_t * );
  196. static void         MediaDelES( vod_t *, vod_media_t *, es_format_t * );
  197. static void* CommandThread( vlc_object_t *p_this );
  198. static void  CommandPush( vod_t *, rtsp_cmd_type_t, vod_media_t *, const char *psz_session,
  199.                           double f_arg, const char *psz_arg );
  200. static rtsp_client_t *RtspClientNew( vod_media_t *, char * );
  201. static rtsp_client_t *RtspClientGet( vod_media_t *, const char * );
  202. static void           RtspClientDel( vod_media_t *, rtsp_client_t * );
  203. static int RtspCallback( httpd_callback_sys_t *, httpd_client_t *,
  204.                          httpd_message_t *, const httpd_message_t * );
  205. static int RtspCallbackES( httpd_callback_sys_t *, httpd_client_t *,
  206.                            httpd_message_t *, const httpd_message_t * );
  207. static char *SDPGenerate( const vod_media_t *, httpd_client_t *cl );
  208. static void sprintf_hexa( char *s, uint8_t *p_data, int i_data )
  209. {
  210.     static const char hex[16] = "0123456789abcdef";
  211.     int i;
  212.     for( i = 0; i < i_data; i++ )
  213.     {
  214.         s[2*i+0] = hex[(p_data[i]>>4)&0xf];
  215.         s[2*i+1] = hex[(p_data[i]   )&0xf];
  216.     }
  217.     s[2*i_data] = '';
  218. }
  219. /*****************************************************************************
  220.  * Open: Starts the RTSP server module
  221.  *****************************************************************************/
  222. static int Open( vlc_object_t *p_this )
  223. {
  224.     vod_t *p_vod = (vod_t *)p_this;
  225.     vod_sys_t *p_sys = NULL;
  226.     char *psz_url = NULL;
  227.     vlc_url_t url;
  228.     psz_url = config_GetPsz( p_vod, "rtsp-host" );
  229.     vlc_UrlParse( &url, psz_url, 0 );
  230.     free( psz_url );
  231.     if( url.i_port <= 0 ) url.i_port = 554;
  232.     p_vod->p_sys = p_sys = malloc( sizeof( vod_sys_t ) );
  233.     if( !p_sys ) goto error;
  234.     p_sys->p_rtsp_host = 0;
  235.     p_sys->i_session_timeout = var_CreateGetInteger( p_this, "rtsp-session-timeout" );
  236.     p_sys->i_throttle_users = var_CreateGetInteger( p_this, "rtsp-throttle-users" );
  237.     msg_Dbg( p_this, "allowing up to %d connections", p_sys->i_throttle_users );
  238.     p_sys->i_connections = 0;
  239.     p_sys->psz_raw_mux = var_CreateGetString( p_this, "rtsp-raw-mux" );
  240.     p_sys->p_rtsp_host =
  241.         httpd_HostNew( VLC_OBJECT(p_vod), url.psz_host, url.i_port );
  242.     if( !p_sys->p_rtsp_host )
  243.     {
  244.         msg_Err( p_vod, "cannot create RTSP server (%s:%i)",
  245.                  url.psz_host, url.i_port );
  246.         goto error;
  247.     }
  248.     p_sys->psz_path = strdup( url.psz_path ? url.psz_path : "/" );
  249.     p_sys->i_port = url.i_port;
  250.     vlc_UrlClean( &url );
  251.     vlc_mutex_init( &p_sys->lock_media );
  252.     TAB_INIT( p_sys->i_media, p_sys->media );
  253.     p_sys->i_media_id = 0;
  254.     p_vod->pf_media_new = MediaNew;
  255.     p_vod->pf_media_del = MediaAskDel;
  256.     p_vod->pf_media_add_es = MediaAddES;
  257.     p_vod->pf_media_del_es = MediaDelES;
  258.     p_sys->p_fifo_cmd = block_FifoNew();
  259.     if( vlc_thread_create( p_vod, "rtsp vod thread", CommandThread,
  260.                            VLC_THREAD_PRIORITY_LOW ) )
  261.     {
  262.         msg_Err( p_vod, "cannot spawn rtsp vod thread" );
  263.         block_FifoRelease( p_sys->p_fifo_cmd );
  264.         free( p_sys->psz_path );
  265.         goto error;
  266.     }
  267.     return VLC_SUCCESS;
  268. error:
  269.     if( p_sys )
  270.     {
  271.         if( p_sys->p_rtsp_host ) httpd_HostDelete( p_sys->p_rtsp_host );
  272.         free( p_sys->psz_raw_mux );
  273.         free( p_sys );
  274.     }
  275.     vlc_UrlClean( &url );
  276.     return VLC_EGENERIC;
  277. }
  278. /*****************************************************************************
  279.  * Close:
  280.  *****************************************************************************/
  281. static void Close( vlc_object_t * p_this )
  282. {
  283.     vod_t *p_vod = (vod_t *)p_this;
  284.     vod_sys_t *p_sys = p_vod->p_sys;
  285.     block_t *p_block_cmd;
  286.     rtsp_cmd_t cmd;
  287.     /* Stop command thread */
  288.     vlc_object_kill( p_vod );
  289.     CommandPush( p_vod, RTSP_CMD_TYPE_NONE, NULL, NULL, 0.0, NULL );
  290.     vlc_thread_join( p_vod );
  291.     while( block_FifoCount( p_sys->p_fifo_cmd ) > 0 )
  292.     {
  293.          p_block_cmd = block_FifoGet( p_sys->p_fifo_cmd );
  294.          memcpy( &cmd, p_block_cmd->p_buffer, sizeof(cmd) );
  295.          block_Release( p_block_cmd );
  296.          if ( cmd.i_type == RTSP_CMD_TYPE_DEL )
  297.              MediaDel(p_vod, cmd.p_media);
  298.          free( cmd.psz_session );
  299.          free( cmd.psz_arg );
  300.     }
  301.     block_FifoRelease( p_sys->p_fifo_cmd );
  302.     httpd_HostDelete( p_sys->p_rtsp_host );
  303.     var_Destroy( p_this, "rtsp-session-timeout" );
  304.     var_Destroy( p_this, "rtsp-throttle-users" );
  305.     var_Destroy( p_this, "rtsp-raw-mux" );
  306.     /* Check VLM is not buggy */
  307.     if( p_sys->i_media > 0 )
  308.         msg_Err( p_vod, "rtsp vod leaking %d medias", p_sys->i_media );
  309.     TAB_CLEAN( p_sys->i_media, p_sys->media );
  310.     vlc_mutex_destroy( &p_sys->lock_media );
  311.     free( p_sys->psz_path );
  312.     free( p_sys->psz_raw_mux );
  313.     free( p_sys );
  314. }
  315. /*****************************************************************************
  316.  * Media handling
  317.  *****************************************************************************/
  318. static vod_media_t *MediaNew( vod_t *p_vod, const char *psz_name,
  319.                               input_item_t *p_item )
  320. {
  321.     int i;
  322.     vod_sys_t *p_sys = p_vod->p_sys;
  323.     vod_media_t *p_media = calloc( 1, sizeof(vod_media_t) );
  324.     if( !p_media )
  325.         return NULL;
  326.     p_media->id = p_sys->i_media_id++;
  327.     TAB_INIT( p_media->i_es, p_media->es );
  328.     p_media->psz_mux = NULL;
  329.     TAB_INIT( p_media->i_rtsp, p_media->rtsp );
  330.     p_media->b_raw = false;
  331.     if( asprintf( &p_media->psz_rtsp_path, "%s%s",
  332.                   p_sys->psz_path, psz_name ) <0 )
  333.         return NULL;
  334.     p_media->p_rtsp_url =
  335.         httpd_UrlNewUnique( p_sys->p_rtsp_host, p_media->psz_rtsp_path, NULL,
  336.                             NULL, NULL );
  337.     if( !p_media->p_rtsp_url )
  338.     {
  339.         msg_Err( p_vod, "cannot create RTSP url (%s)", p_media->psz_rtsp_path);
  340.         free( p_media->psz_rtsp_path );
  341.         free( p_media );
  342.         return NULL;
  343.     }
  344.     msg_Dbg( p_vod, "created RTSP url: %s", p_media->psz_rtsp_path );
  345.     if( asprintf( &p_media->psz_rtsp_control_v4,
  346.                "a=control:rtsp://%%s:%d%s/trackID=%%drn",
  347.                p_sys->i_port, p_media->psz_rtsp_path ) < 0 )
  348.     {
  349.         httpd_UrlDelete( p_media->p_rtsp_url );
  350.         free( p_media->psz_rtsp_path );
  351.         free( p_media );
  352.         return NULL;
  353.     }
  354.     if( asprintf( &p_media->psz_rtsp_control_v6,
  355.                "a=control:rtsp://[%%s]:%d%s/trackID=%%drn",
  356.               p_sys->i_port, p_media->psz_rtsp_path ) < 0 )
  357.     {
  358.         httpd_UrlDelete( p_media->p_rtsp_url );
  359.         free( p_media->psz_rtsp_path );
  360.         free( p_media );
  361.         return NULL;
  362.     }
  363.     httpd_UrlCatch( p_media->p_rtsp_url, HTTPD_MSG_SETUP,
  364.                     RtspCallback, (void*)p_media );
  365.     httpd_UrlCatch( p_media->p_rtsp_url, HTTPD_MSG_DESCRIBE,
  366.                     RtspCallback, (void*)p_media );
  367.     httpd_UrlCatch( p_media->p_rtsp_url, HTTPD_MSG_PLAY,
  368.                     RtspCallback, (void*)p_media );
  369.     httpd_UrlCatch( p_media->p_rtsp_url, HTTPD_MSG_PAUSE,
  370.                     RtspCallback, (void*)p_media );
  371.     httpd_UrlCatch( p_media->p_rtsp_url, HTTPD_MSG_GETPARAMETER,
  372.                     RtspCallback, (void*)p_media );
  373.     httpd_UrlCatch( p_media->p_rtsp_url, HTTPD_MSG_TEARDOWN,
  374.                     RtspCallback, (void*)p_media );
  375.     p_media->p_vod = p_vod;
  376.     vlc_mutex_init( &p_media->lock );
  377.     p_media->psz_session_name = strdup("");
  378.     p_media->psz_session_description = strdup("");
  379.     p_media->psz_session_url = strdup("");
  380.     p_media->psz_session_email = strdup("");
  381.     p_media->i_port_audio = 1234;
  382.     p_media->i_port_video = 1236;
  383.     p_media->i_port       = 1238;
  384.     p_media->i_payload_type = 96;
  385.     p_media->i_sdp_id = mdate();
  386.     p_media->i_sdp_version = 1;
  387.     p_media->i_length = input_item_GetDuration( p_item );
  388.     vlc_mutex_lock( &p_item->lock );
  389.     msg_Dbg( p_vod, "media has %i declared ES", p_item->i_es );
  390.     for( i = 0; i < p_item->i_es; i++ )
  391.     {
  392.         MediaAddES( p_vod, p_media, p_item->es[i] );
  393.     }
  394.     vlc_mutex_unlock( &p_item->lock );
  395.     CommandPush( p_vod, RTSP_CMD_TYPE_ADD, p_media, NULL, 0.0, NULL );
  396.     return p_media;
  397. }
  398. static void MediaAskDel ( vod_t *p_vod, vod_media_t *p_media )
  399. {
  400.     CommandPush( p_vod, RTSP_CMD_TYPE_DEL, p_media, NULL, 0.0, NULL );
  401. }
  402. static void MediaDel( vod_t *p_vod, vod_media_t *p_media )
  403. {
  404.     vod_sys_t *p_sys = p_vod->p_sys;
  405.     msg_Dbg( p_vod, "deleting media: %s", p_media->psz_rtsp_path );
  406.     vlc_mutex_lock( &p_sys->lock_media );
  407.     TAB_REMOVE( p_sys->i_media, p_sys->media, p_media );
  408.     vlc_mutex_unlock( &p_sys->lock_media );
  409.     httpd_UrlDelete( p_media->p_rtsp_url );
  410.     while( p_media->i_rtsp > 0 )
  411.         RtspClientDel( p_media, p_media->rtsp[0] );
  412.     TAB_CLEAN( p_media->i_rtsp, p_media->rtsp );
  413.     free( p_media->psz_rtsp_path );
  414.     free( p_media->psz_rtsp_control_v6 );
  415.     free( p_media->psz_rtsp_control_v4 );
  416.     while( p_media->i_es )
  417.         MediaDelES( p_vod, p_media, &p_media->es[0]->fmt );
  418.     TAB_CLEAN( p_media->i_es, p_media->es );
  419.     vlc_mutex_destroy( &p_media->lock );
  420.     free( p_media->psz_session_name );
  421.     free( p_media->psz_session_description );
  422.     free( p_media->psz_session_url );
  423.     free( p_media->psz_session_email );
  424.     free( p_media->psz_mux );
  425.     free( p_media );
  426. }
  427. static int MediaAddES( vod_t *p_vod, vod_media_t *p_media, es_format_t *p_fmt )
  428. {
  429.     char *psz_urlc;
  430.     media_es_t *p_es = calloc( 1, sizeof(media_es_t) );
  431.     if( !p_es )
  432.         return VLC_ENOMEM;
  433.     free( p_media->psz_mux );
  434.     p_media->psz_mux = NULL;
  435.     /* TODO: update SDP, etc... */
  436.     if( asprintf( &psz_urlc, "%s/trackID=%d",
  437.               p_media->psz_rtsp_path, p_media->i_es ) < 0 )
  438.     {
  439.         free( p_es );
  440.         return VLC_ENOMEM;
  441.     }
  442.     msg_Dbg( p_vod, "  - ES %4.4s (%s)", (char *)&p_fmt->i_codec, psz_urlc );
  443.     switch( p_fmt->i_codec )
  444.     {
  445.         case VLC_FOURCC( 's', '1', '6', 'b' ):
  446.             if( p_fmt->audio.i_channels == 1 && p_fmt->audio.i_rate == 44100 )
  447.             {
  448.                 p_es->i_payload_type = 11;
  449.             }
  450.             else if( p_fmt->audio.i_channels == 2 &&
  451.                      p_fmt->audio.i_rate == 44100 )
  452.             {
  453.                 p_es->i_payload_type = 10;
  454.             }
  455.             else
  456.             {
  457.                 p_es->i_payload_type = p_media->i_payload_type++;
  458.             }
  459.             if( asprintf( &p_es->psz_rtpmap, "L16/%d/%d", p_fmt->audio.i_rate,
  460.                           p_fmt->audio.i_channels ) == -1 )
  461.                 p_es->psz_rtpmap = NULL;
  462.             break;
  463.         case VLC_FOURCC( 'u', '8', ' ', ' ' ):
  464.             p_es->i_payload_type = p_media->i_payload_type++;
  465.             if( asprintf( &p_es->psz_rtpmap, "L8/%d/%d", p_fmt->audio.i_rate,
  466.                           p_fmt->audio.i_channels ) == -1 )
  467.                 p_es->psz_rtpmap = NULL;
  468.             break;
  469.         case VLC_FOURCC( 'm', 'p', 'g', 'a' ):
  470.         case VLC_FOURCC( 'm', 'p', '3', ' ' ):
  471.             p_es->i_payload_type = 14;
  472.             p_es->psz_rtpmap = strdup( "MPA/90000" );
  473.             break;
  474.         case VLC_FOURCC( 'm', 'p', 'g', 'v' ):
  475.             p_es->i_payload_type = 32;
  476.             p_es->psz_rtpmap = strdup( "MPV/90000" );
  477.             break;
  478.         case VLC_FOURCC( 'a', '5', '2', ' ' ):
  479.             p_es->i_payload_type = p_media->i_payload_type++;
  480.             if( asprintf( &p_es->psz_rtpmap, "ac3/%d", p_fmt->audio.i_rate ) == -1 )
  481.                 p_es->psz_rtpmap = NULL;
  482.             break;
  483.         case VLC_FOURCC( 'H', '2', '6', '3' ):
  484.             p_es->i_payload_type = p_media->i_payload_type++;
  485.             p_es->psz_rtpmap = strdup( "H263-1998/90000" );
  486.             break;
  487.         case VLC_FOURCC( 'h', '2', '6', '4' ):
  488.             p_es->i_payload_type = p_media->i_payload_type++;
  489.             p_es->psz_rtpmap = strdup( "H264/90000" );
  490.             p_es->psz_fmtp = NULL;
  491.             /* FIXME AAAAAAAAAAAARRRRRRRRGGGG copied from stream_out/rtp.c */
  492.             if( p_fmt->i_extra > 0 )
  493.             {
  494.                 uint8_t *p_buffer = p_fmt->p_extra;
  495.                 int     i_buffer = p_fmt->i_extra;
  496.                 char    *p_64_sps = NULL;
  497.                 char    *p_64_pps = NULL;
  498.                 char    hexa[6+1];
  499.                 while( i_buffer > 4 &&
  500.                        p_buffer[0] == 0 && p_buffer[1] == 0 &&
  501.                        p_buffer[2] == 0 && p_buffer[3] == 1 )
  502.                 {
  503.                     const int i_nal_type = p_buffer[4]&0x1f;
  504.                     int i_offset;
  505.                     int i_size      = 0;
  506.                     i_size = i_buffer;
  507.                     for( i_offset = 4; i_offset+3 < i_buffer ; i_offset++)
  508.                     {
  509.                         if( p_buffer[i_offset] == 0 && p_buffer[i_offset+1] == 0 && p_buffer[i_offset+2] == 0 && p_buffer[i_offset+3] == 1 )
  510.                         {
  511.                             /* we found another startcode */
  512.                             i_size = i_offset;
  513.                             break;
  514.                         }
  515.                     }
  516.                     if( i_nal_type == 7 )
  517.                     {
  518.                         free( p_64_sps );
  519.                         p_64_sps = vlc_b64_encode_binary( &p_buffer[4], i_size - 4 );
  520.                         sprintf_hexa( hexa, &p_buffer[5], 3 );
  521.                     }
  522.                     else if( i_nal_type == 8 )
  523.                     {
  524.                         free( p_64_pps );
  525.                         p_64_pps = vlc_b64_encode_binary( &p_buffer[4], i_size - 4 );
  526.                     }
  527.                     i_buffer -= i_size;
  528.                     p_buffer += i_size;
  529.                 }
  530.                 /* */
  531.                 if( p_64_sps && p_64_pps )
  532.                 {
  533.                     if( asprintf( &p_es->psz_fmtp,
  534.                                   "packetization-mode=1;profile-level-id=%s;"
  535.                                   "sprop-parameter-sets=%s,%s;", hexa, p_64_sps,
  536.                                   p_64_pps ) < 0 )
  537.                     {
  538.                         free( p_64_sps );
  539.                         free( p_64_pps );
  540.                         free( psz_urlc );
  541.                         free( p_es );
  542.                         return VLC_ENOMEM;
  543.                     }
  544.                 }
  545.                 free( p_64_sps );
  546.                 free( p_64_pps );
  547.             }
  548.             if( !p_es->psz_fmtp )
  549.                 p_es->psz_fmtp = strdup( "packetization-mode=1" );
  550.             break;
  551.         case VLC_FOURCC( 'm', 'p', '4', 'v' ):
  552.             p_es->i_payload_type = p_media->i_payload_type++;
  553.             p_es->psz_rtpmap = strdup( "MP4V-ES/90000" );
  554.             if( p_fmt->i_extra > 0 )
  555.             {
  556.                 char *p_hexa = malloc( 2 * p_fmt->i_extra + 1 );
  557.                 sprintf_hexa( p_hexa, p_fmt->p_extra, p_fmt->i_extra );
  558.                 if( asprintf( &p_es->psz_fmtp,
  559.                               "profile-level-id=3; config=%s;", p_hexa ) == -1 )
  560.                     p_es->psz_fmtp = NULL;
  561.                 free( p_hexa );
  562.             }
  563.             break;
  564.         case VLC_FOURCC( 'm', 'p', '4', 'a' ):
  565.             p_es->i_payload_type = p_media->i_payload_type++;
  566.             if( asprintf( &p_es->psz_rtpmap, "mpeg4-generic/%d", p_fmt->audio.i_rate ) == -1 )
  567.                 p_es->psz_rtpmap = NULL;
  568.             if( p_fmt->i_extra > 0 )
  569.             {
  570.                 char *p_hexa = malloc( 2 * p_fmt->i_extra + 1 );
  571.                 sprintf_hexa( p_hexa, p_fmt->p_extra, p_fmt->i_extra );
  572.                 if( asprintf( &p_es->psz_fmtp,
  573.                               "streamtype=5; profile-level-id=15; mode=AAC-hbr; "
  574.                               "config=%s; SizeLength=13;IndexLength=3; "
  575.                               "IndexDeltaLength=3; Profile=1;", p_hexa ) == -1 )
  576.                     p_es->psz_fmtp = NULL;
  577.                 free( p_hexa );
  578.             }
  579.             break;
  580.         case VLC_FOURCC( 'm', 'p', '2', 't' ):
  581.             p_media->psz_mux = strdup("ts");
  582.             p_es->i_payload_type = 33;
  583.             p_es->psz_rtpmap = strdup( "MP2T/90000" );
  584.             break;
  585.         case VLC_FOURCC( 'm', 'p', '2', 'p' ):
  586.             p_media->psz_mux = strdup("ps");
  587.             p_es->i_payload_type = p_media->i_payload_type++;
  588.             p_es->psz_rtpmap = strdup( "MP2P/90000" );
  589.             break;
  590.         case VLC_FOURCC( 's', 'a', 'm', 'r' ):
  591.             p_es->i_payload_type = p_media->i_payload_type++;
  592.             p_es->psz_rtpmap = strdup( p_fmt->audio.i_channels == 2 ?
  593.                                     "AMR/8000/2" : "AMR/8000" );
  594.             p_es->psz_fmtp = strdup( "octet-align=1" );
  595.             break;
  596.         case VLC_FOURCC( 's', 'a', 'w', 'b' ):
  597.             p_es->i_payload_type = p_media->i_payload_type++;
  598.             p_es->psz_rtpmap = strdup( p_fmt->audio.i_channels == 2 ?
  599.                                     "AMR-WB/16000/2" : "AMR-WB/16000" );
  600.             p_es->psz_fmtp = strdup( "octet-align=1" );
  601.             break;
  602.         default:
  603.             msg_Err( p_vod, "cannot add this stream (unsupported "
  604.                     "codec: %4.4s)", (char*)&p_fmt->i_codec );
  605.             free( psz_urlc );
  606.             free( p_es );
  607.             return VLC_EGENERIC;
  608.     }
  609.     p_es->p_rtsp_url =
  610.         httpd_UrlNewUnique( p_vod->p_sys->p_rtsp_host, psz_urlc, NULL, NULL,
  611.                             NULL );
  612.     if( !p_es->p_rtsp_url )
  613.     {
  614.         msg_Err( p_vod, "cannot create RTSP url (%s)", psz_urlc );
  615.         free( psz_urlc );
  616.         free( p_es );
  617.         return VLC_EGENERIC;
  618.     }
  619.     free( psz_urlc );
  620.     httpd_UrlCatch( p_es->p_rtsp_url, HTTPD_MSG_SETUP,
  621.                     RtspCallbackES, (void*)p_es );
  622.     httpd_UrlCatch( p_es->p_rtsp_url, HTTPD_MSG_TEARDOWN,
  623.                     RtspCallbackES, (void*)p_es );
  624.     httpd_UrlCatch( p_es->p_rtsp_url, HTTPD_MSG_PLAY,
  625.                     RtspCallbackES, (void*)p_es );
  626.     httpd_UrlCatch( p_es->p_rtsp_url, HTTPD_MSG_PAUSE,
  627.                     RtspCallbackES, (void*)p_es );
  628.     es_format_Copy( &p_es->fmt, p_fmt );
  629.     p_es->p_vod = p_vod;
  630.     p_es->p_media = p_media;
  631. #if 0
  632.     /* Choose the port */
  633.     if( p_fmt->i_cat == AUDIO_ES && p_media->i_port_audio > 0 )
  634.     {
  635.         p_es->i_port = p_media->i_port_audio;
  636.         p_media->i_port_audio = 0;
  637.     }
  638.     else if( p_fmt->i_cat == VIDEO_ES && p_media->i_port_video > 0 )
  639.     {
  640.         p_es->i_port = p_media->i_port_video;
  641.         p_media->i_port_video = 0;
  642.     }
  643.     while( !p_es->i_port )
  644.     {
  645.         if( p_media->i_port != p_media->i_port_audio &&
  646.             p_media->i_port != p_media->i_port_video )
  647.         {
  648.             p_es->i_port = p_media->i_port;
  649.             p_media->i_port += 2;
  650.             break;
  651.         }
  652.         p_media->i_port += 2;
  653.     }
  654. #else
  655.     p_es->i_port = 0;
  656. #endif
  657.     vlc_mutex_lock( &p_media->lock );
  658.     TAB_APPEND( p_media->i_es, p_media->es, p_es );
  659.     vlc_mutex_unlock( &p_media->lock );
  660.     p_media->i_sdp_version++;
  661.     return VLC_SUCCESS;
  662. }
  663. static void MediaDelES( vod_t *p_vod, vod_media_t *p_media, es_format_t *p_fmt)
  664. {
  665.     media_es_t *p_es = NULL;
  666.     int i;
  667.     /* Find the ES */
  668.     for( i = 0; i < p_media->i_es; i++ )
  669.     {
  670.         if( p_media->es[i]->fmt.i_cat == p_fmt->i_cat &&
  671.             p_media->es[i]->fmt.i_codec == p_fmt->i_codec &&
  672.             p_media->es[i]->fmt.i_id == p_fmt->i_id )
  673.         {
  674.             p_es = p_media->es[i];
  675.         }
  676.     }
  677.     if( !p_es ) return;
  678.     msg_Dbg( p_vod, "  - Removing ES %4.4s", (char *)&p_fmt->i_codec );
  679.     vlc_mutex_lock( &p_media->lock );
  680.     TAB_REMOVE( p_media->i_es, p_media->es, p_es );
  681.     vlc_mutex_unlock( &p_media->lock );
  682.     free( p_es->psz_rtpmap );
  683.     free( p_es->psz_fmtp );
  684.     p_media->i_sdp_version++;
  685.     if( p_es->p_rtsp_url ) httpd_UrlDelete( p_es->p_rtsp_url );
  686.     es_format_Clean( &p_es->fmt );
  687.     free( p_es );
  688. }
  689. static void CommandPush( vod_t *p_vod, rtsp_cmd_type_t i_type, vod_media_t *p_media, const char *psz_session,
  690.                          double f_arg, const char *psz_arg )
  691. {
  692.     rtsp_cmd_t cmd;
  693.     block_t *p_cmd;
  694.     memset( &cmd, 0, sizeof(cmd) );
  695.     cmd.i_type = i_type;
  696.     cmd.p_media = p_media;
  697.     if( p_media )
  698.         cmd.i_media_id = p_media->id;
  699.     if( psz_session )
  700.         cmd.psz_session = strdup(psz_session);
  701.     cmd.f_arg = f_arg;
  702.     if( psz_arg )
  703.         cmd.psz_arg = strdup(psz_arg);
  704.     p_cmd = block_New( p_vod, sizeof(rtsp_cmd_t) );
  705.     memcpy( p_cmd->p_buffer, &cmd, sizeof(cmd) );
  706.     block_FifoPut( p_vod->p_sys->p_fifo_cmd, p_cmd );
  707. }
  708. static void* CommandThread( vlc_object_t *p_this )
  709. {
  710.     vod_t *p_vod = (vod_t*)p_this;
  711.     vod_sys_t *p_sys = p_vod->p_sys;
  712.     int canc = vlc_savecancel ();
  713.     while( vlc_object_alive (p_vod) )
  714.     {
  715.         block_t *p_block_cmd = block_FifoGet( p_sys->p_fifo_cmd );
  716.         rtsp_cmd_t cmd;
  717.         vod_media_t *p_media = NULL;
  718.         int i;
  719.         if( !p_block_cmd )
  720.             break;
  721.         memcpy( &cmd, p_block_cmd->p_buffer, sizeof(cmd) );
  722.         block_Release( p_block_cmd );
  723.         if( cmd.i_type == RTSP_CMD_TYPE_NONE )
  724.             break;
  725.         if ( cmd.i_type == RTSP_CMD_TYPE_ADD )
  726.         {
  727.             vlc_mutex_lock( &p_sys->lock_media );
  728.             TAB_APPEND( p_sys->i_media, p_sys->media, cmd.p_media );
  729.             vlc_mutex_unlock( &p_sys->lock_media );
  730.             goto next;
  731.         }
  732.         if ( cmd.i_type == RTSP_CMD_TYPE_DEL )
  733.         {
  734.             MediaDel(p_vod, cmd.p_media);
  735.             goto next;
  736.         }
  737.         /* */
  738.         vlc_mutex_lock( &p_sys->lock_media );
  739.         for( i = 0; i < p_sys->i_media; i++ )
  740.         {
  741.             if( p_sys->media[i]->id == cmd.i_media_id )
  742.                 break;
  743.         }
  744.         if( i >= p_sys->i_media )
  745.         {
  746.             vlc_mutex_unlock( &p_sys->lock_media );
  747.             goto next;
  748.         }
  749.         p_media = p_sys->media[i];
  750.         switch( cmd.i_type )
  751.         {
  752.         case RTSP_CMD_TYPE_PLAY:
  753.             vod_MediaControl( p_vod, p_media, cmd.psz_session,
  754.                               VOD_MEDIA_PLAY, cmd.psz_arg );
  755.             break;
  756.         case RTSP_CMD_TYPE_PAUSE:
  757.             vod_MediaControl( p_vod, p_media, cmd.psz_session,
  758.                               VOD_MEDIA_PAUSE );
  759.             break;
  760.         case RTSP_CMD_TYPE_STOP:
  761.             vod_MediaControl( p_vod, p_media, cmd.psz_session, VOD_MEDIA_STOP );
  762.             break;
  763.         case RTSP_CMD_TYPE_SEEK:
  764.             vod_MediaControl( p_vod, p_media, cmd.psz_session,
  765.                               VOD_MEDIA_SEEK, cmd.f_arg );
  766.             break;
  767.         case RTSP_CMD_TYPE_REWIND:
  768.             vod_MediaControl( p_vod, p_media, cmd.psz_session,
  769.                               VOD_MEDIA_REWIND, cmd.f_arg );
  770.             break;
  771.         case RTSP_CMD_TYPE_FORWARD:
  772.             vod_MediaControl( p_vod, p_media, cmd.psz_session,
  773.                               VOD_MEDIA_FORWARD, cmd.f_arg );
  774.             break;
  775.         default:
  776.             break;
  777.         }
  778.         vlc_mutex_unlock( &p_sys->lock_media );
  779.     next:
  780.         free( cmd.psz_session );
  781.         free( cmd.psz_arg );
  782.     }
  783.     vlc_restorecancel (canc);
  784.     return NULL;
  785. }
  786. /****************************************************************************
  787.  * RTSP server implementation
  788.  ****************************************************************************/
  789. static rtsp_client_t *RtspClientNew( vod_media_t *p_media, char *psz_session )
  790. {
  791.     rtsp_client_t *p_rtsp = calloc( 1, sizeof(rtsp_client_t) );
  792.     if( !p_rtsp )
  793.         return NULL;
  794.     p_rtsp->es = 0;
  795.     p_rtsp->psz_session = psz_session;
  796.     TAB_APPEND( p_media->i_rtsp, p_media->rtsp, p_rtsp );
  797.     p_media->p_vod->p_sys->i_connections++;
  798.     msg_Dbg( p_media->p_vod, "new session: %s, connections: %d",
  799.              psz_session, p_media->p_vod->p_sys->i_throttle_users );
  800.     return p_rtsp;
  801. }
  802. static rtsp_client_t *RtspClientGet( vod_media_t *p_media, const char *psz_session )
  803. {
  804.     int i;
  805.     for( i = 0; psz_session && i < p_media->i_rtsp; i++ )
  806.     {
  807.         if( !strcmp( p_media->rtsp[i]->psz_session, psz_session ) )
  808.             return p_media->rtsp[i];
  809.     }
  810.     return NULL;
  811. }
  812. static void RtspClientDel( vod_media_t *p_media, rtsp_client_t *p_rtsp )
  813. {
  814.     p_media->p_vod->p_sys->i_connections--;
  815.     msg_Dbg( p_media->p_vod, "closing session: %s, connections: %d",
  816.              p_rtsp->psz_session, p_media->p_vod->p_sys->i_throttle_users );
  817.     while( p_rtsp->i_es )
  818.     {
  819.         p_rtsp->i_es--;
  820.         free( p_rtsp->es[p_rtsp->i_es]->psz_ip );
  821.         free( p_rtsp->es[p_rtsp->i_es] );
  822.     }
  823.     free( p_rtsp->es );
  824.     TAB_REMOVE( p_media->i_rtsp, p_media->rtsp, p_rtsp );
  825.     free( p_rtsp->psz_session );
  826.     free( p_rtsp );
  827. }
  828. static float ParseNPT (const char *str)
  829. {
  830.      locale_t loc = newlocale (LC_NUMERIC_MASK, "C", NULL);
  831.      locale_t oldloc = uselocale (loc);
  832.      unsigned hour, min;
  833.      float sec;
  834.      if (sscanf (str, "%u:%u:%f", &hour, &min, &sec) == 3)
  835.          sec += ((hour * 60) + min) * 60;
  836.      else
  837.      if (sscanf (str, "%f", &sec) != 1)
  838.          sec = 0.;
  839.      if (loc != (locale_t)0)
  840.      {
  841.          uselocale (oldloc);
  842.          freelocale (loc);
  843.      }
  844.      return sec;
  845. }
  846. static int RtspCallback( httpd_callback_sys_t *p_args, httpd_client_t *cl,
  847.                          httpd_message_t *answer, const httpd_message_t *query )
  848. {
  849.     vod_media_t *p_media = (vod_media_t*)p_args;
  850.     vod_t *p_vod = p_media->p_vod;
  851.     const char *psz_transport = NULL;
  852.     const char *psz_playnow = NULL; /* support option: x-playNow */
  853.     const char *psz_session = NULL;
  854.     const char *psz_cseq = NULL;
  855.     rtsp_client_t *p_rtsp;
  856.     int i_port = 0;
  857.     int i_cseq = 0;
  858.     if( answer == NULL || query == NULL ) return VLC_SUCCESS;
  859.     msg_Dbg( p_vod, "RtspCallback query: type=%d", query->i_type );
  860.     answer->i_proto   = HTTPD_PROTO_RTSP;
  861.     answer->i_version = query->i_version;
  862.     answer->i_type    = HTTPD_MSG_ANSWER;
  863.     answer->i_body    = 0;
  864.     answer->p_body    = NULL;
  865.     switch( query->i_type )
  866.     {
  867.         case HTTPD_MSG_SETUP:
  868.         {
  869.             psz_playnow = httpd_MsgGet( query, "x-playNow" );
  870.             psz_transport = httpd_MsgGet( query, "Transport" );
  871.             if( psz_transport == NULL )
  872.             {
  873.                 answer->i_status = 400;
  874.                 break;
  875.             }
  876.             msg_Dbg( p_vod, "HTTPD_MSG_SETUP: transport=%s", psz_transport );
  877.             if( strstr( psz_transport, "unicast" ) &&
  878.                 strstr( psz_transport, "client_port=" ) )
  879.             {
  880.                 rtsp_client_t *p_rtsp = NULL;
  881.                 char ip[NI_MAXNUMERICHOST];
  882.                 i_port = atoi( strstr( psz_transport, "client_port=" ) +
  883.                                 strlen("client_port=") );
  884.                 if( strstr( psz_transport, "MP2T/H2221/UDP" ) ||
  885.                     strstr( psz_transport, "RAW/RAW/UDP" ) )
  886.                 {
  887.                     free( p_media->psz_mux );
  888.                     p_media->psz_mux = NULL;
  889.                     p_media->psz_mux = strdup( p_vod->p_sys->psz_raw_mux );
  890.                     p_media->b_raw = true;
  891.                 }
  892.                 if( httpd_ClientIP( cl, ip ) == NULL )
  893.                 {
  894.                     answer->i_status = 500;
  895.                     answer->i_body = 0;
  896.                     answer->p_body = NULL;
  897.                     break;
  898.                 }
  899.                 msg_Dbg( p_vod, "HTTPD_MSG_SETUP: unicast ip=%s port=%d",
  900.                          ip, i_port );
  901.                 psz_session = httpd_MsgGet( query, "Session" );
  902.                 if( !psz_session || !*psz_session )
  903.                 {
  904.                     char *psz_new;
  905.                     if( ( p_vod->p_sys->i_throttle_users > 0 ) &&
  906.                         ( p_vod->p_sys->i_connections >= p_vod->p_sys->i_throttle_users ) )
  907.                     {
  908.                         answer->i_status = 503;
  909.                         answer->i_body = 0;
  910.                         answer->p_body = NULL;
  911.                         break;
  912.                     }
  913.                     if( asprintf( &psz_new, "%d", rand() ) < 0 )
  914.                         return VLC_ENOMEM;
  915.                     psz_session = psz_new;
  916.                     p_rtsp = RtspClientNew( p_media, psz_new );
  917.                     if( !p_rtsp )
  918.                     {
  919.                         answer->i_status = 454;
  920.                         answer->i_body = 0;
  921.                         answer->p_body = NULL;
  922.                         break;
  923.                     }
  924.                 }
  925.                 else
  926.                 {
  927.                     p_rtsp = RtspClientGet( p_media, psz_session );
  928.                     if( !p_rtsp )
  929.                     {
  930.                         answer->i_status = 454;
  931.                         answer->i_body = 0;
  932.                         answer->p_body = NULL;
  933.                         break;
  934.                     }
  935.                 }
  936.                 answer->i_status = 200;
  937.                 answer->i_body = 0;
  938.                 answer->p_body = NULL;
  939.                 if( p_media->b_raw )
  940.                 {
  941.                     if( strstr( psz_transport, "MP2T/H2221/UDP" ) )
  942.                     {
  943.                         httpd_MsgAdd( answer, "Transport",
  944.                                       "MP2T/H2221/UDP;unicast;client_port=%d-%d",
  945.                                       i_port, i_port + 1 );
  946.                     }
  947.                     else if( strstr( psz_transport, "RAW/RAW/UDP" ) )
  948.                     {
  949.                         httpd_MsgAdd( answer, "Transport",
  950.                                       "RAW/RAW/UDP;unicast;client_port=%d-%d",
  951.                                       i_port, i_port + 1 );
  952.                     }
  953.                 }
  954.                 else
  955.                     httpd_MsgAdd( answer, "Transport",
  956.                                   "RTP/AVP/UDP;unicast;client_port=%d-%d",
  957.                                   i_port, i_port + 1 );
  958.             }
  959.             else /* TODO  strstr( psz_transport, "interleaved" ) ) */
  960.             {
  961.                 answer->i_status = 461;
  962.                 answer->i_body = 0;
  963.                 answer->p_body = NULL;
  964.             }
  965.             /* Intentional fall-through on x-playNow option in RTSP request */
  966.             if( !psz_playnow )
  967.                 break;
  968.         }
  969.         case HTTPD_MSG_PLAY:
  970.         {
  971.             char *psz_output, ip[NI_MAXNUMERICHOST];
  972.             int i, i_port_audio = 0, i_port_video = 0;
  973.             /* for now only multicast so easy */
  974.             if( !psz_playnow )
  975.             {
  976.                 answer->i_status = 200;
  977.                 answer->i_body = 0;
  978.                 answer->p_body = NULL;
  979.             }
  980.             if( !psz_session )
  981.                 psz_session = httpd_MsgGet( query, "Session" );
  982.             msg_Dbg( p_vod, "HTTPD_MSG_PLAY for session: %s", psz_session );
  983.             p_rtsp = RtspClientGet( p_media, psz_session );
  984.             if( !p_rtsp )
  985.             {
  986.                 answer->i_status = 500;
  987.                 answer->i_body = 0;
  988.                 answer->p_body = NULL;
  989.                 break;
  990.             }
  991.             if( p_rtsp->b_playing )
  992.             {
  993.                 const char *psz_position = httpd_MsgGet( query, "Range" );
  994.                 const char *psz_scale = httpd_MsgGet( query, "Scale" );
  995.                 if( psz_position )
  996.                     psz_position = strstr( psz_position, "npt=" );
  997.                 if( psz_position && !psz_scale )
  998.                 {
  999.                     double f_pos = ParseNPT (psz_position + 4);
  1000.                     msg_Dbg( p_vod, "seeking request: %s", psz_position );
  1001.                     f_pos /= ((double)(p_media->i_length))/1000 /1000 / 100;
  1002.                     CommandPush( p_vod, RTSP_CMD_TYPE_SEEK, p_media,
  1003.                                  psz_session, f_pos, NULL );
  1004.                     break;
  1005.                 }
  1006.                 if( psz_scale )
  1007.                 {
  1008.                     double f_scale = 0.0;
  1009.                     char *end;
  1010.                     f_scale = us_strtod( psz_scale, &end );
  1011.                     if( end > psz_scale )
  1012.                     {
  1013.                         f_scale = (f_scale * 30.0);
  1014.                         if( psz_scale[0] == '-' ) /* rewind */
  1015.                         {
  1016.                             msg_Dbg( p_vod, "rewind request: %s", psz_scale );
  1017.                             CommandPush( p_vod, RTSP_CMD_TYPE_REWIND, p_media,
  1018.                                          psz_session, f_scale, NULL );
  1019.                         }
  1020.                         else if(psz_scale[0] != '1' ) /* fast-forward */
  1021.                         {
  1022.                             msg_Dbg( p_vod, "fastforward request: %s",
  1023.                                      psz_scale );
  1024.                             CommandPush( p_vod, RTSP_CMD_TYPE_FORWARD, p_media,
  1025.                                          psz_session, f_scale, NULL );
  1026.                         }
  1027.                         if( p_rtsp->b_paused == true )
  1028.                         {
  1029.                             p_rtsp->b_paused = false;
  1030.                             CommandPush( p_vod, RTSP_CMD_TYPE_PAUSE, p_media,
  1031.                                          psz_session, 0, NULL );
  1032.                         }
  1033.                     }
  1034.                     break;
  1035.                 }
  1036.             }
  1037.             if( p_rtsp->b_playing && p_rtsp->b_paused )
  1038.             {
  1039.                 CommandPush( p_vod, RTSP_CMD_TYPE_PAUSE, p_media,
  1040.                              psz_session, 0, NULL );
  1041.                 p_rtsp->b_paused = false;
  1042.                 break;
  1043.             }
  1044.             else if( p_rtsp->b_playing ) break;
  1045.             if( httpd_ClientIP( cl, ip ) == NULL ) break;
  1046.             p_rtsp->b_playing = true;
  1047.             /* FIXME for != 1 video and 1 audio */
  1048.             for( i = 0; i < p_rtsp->i_es; i++ )
  1049.             {
  1050.                 if( p_rtsp->es[i]->p_media_es->fmt.i_cat == AUDIO_ES )
  1051.                     i_port_audio = p_rtsp->es[i]->i_port;
  1052.                 if( p_rtsp->es[i]->p_media_es->fmt.i_cat == VIDEO_ES )
  1053.                     i_port_video = p_rtsp->es[i]->i_port;
  1054.             }
  1055.             if( p_media->psz_mux )
  1056.             {
  1057.                 if( p_media->b_raw )
  1058.                 {
  1059.                     if( asprintf( &psz_output,
  1060.                               "std{access=udp,dst=%s:%i,mux=%s}",
  1061.                               ip, i_port, p_media->psz_mux ) < 0 )
  1062.                         return VLC_ENOMEM;
  1063.                 }
  1064.                 else
  1065.                 {
  1066.                     if( asprintf( &psz_output,
  1067.                               "rtp{dst=%s,port=%i,mux=%s}",
  1068.                               ip, i_port_video, p_media->psz_mux ) < 0 )
  1069.                         return VLC_ENOMEM;
  1070.                 }
  1071.             }
  1072.             else
  1073.             {
  1074.                 if( asprintf( &psz_output,
  1075.                               "rtp{dst=%s,port-video=%i,port-audio=%i}",
  1076.                               ip, i_port_video, i_port_audio ) < 0 )
  1077.                     return VLC_ENOMEM;
  1078.             }
  1079.             CommandPush( p_vod, RTSP_CMD_TYPE_PLAY, p_media, psz_session,
  1080.                          0, psz_output );
  1081.             free( psz_output );
  1082.             break;
  1083.         }
  1084.         case HTTPD_MSG_DESCRIBE:
  1085.         {
  1086.             char *psz_sdp =
  1087.                 SDPGenerate( p_media, cl );
  1088.             if( psz_sdp != NULL )
  1089.             {
  1090.                 answer->i_status = 200;
  1091.                 httpd_MsgAdd( answer, "Content-type",  "%s",
  1092.                               "application/sdp" );
  1093.                 answer->p_body = (uint8_t *)psz_sdp;
  1094.                 answer->i_body = strlen( psz_sdp );
  1095.             }
  1096.             else
  1097.             {
  1098.                 answer->i_status = 500;
  1099.                 answer->p_body = NULL;
  1100.                 answer->i_body = 0;
  1101.             }
  1102.             break;
  1103.         }
  1104.         case HTTPD_MSG_PAUSE:
  1105.             psz_session = httpd_MsgGet( query, "Session" );
  1106.             msg_Dbg( p_vod, "HTTPD_MSG_PAUSE for session: %s", psz_session );
  1107.             p_rtsp = RtspClientGet( p_media, psz_session );
  1108.             if( !p_rtsp ) break;
  1109.             CommandPush( p_vod, RTSP_CMD_TYPE_PAUSE, p_media, psz_session,
  1110.                          0, NULL );
  1111.             p_rtsp->b_paused = true;
  1112.             answer->i_status = 200;
  1113.             answer->i_body = 0;
  1114.             answer->p_body = NULL;
  1115.             break;
  1116.         case HTTPD_MSG_TEARDOWN:
  1117.             /* for now only multicast so easy again */
  1118.             answer->i_status = 200;
  1119.             answer->i_body = 0;
  1120.             answer->p_body = NULL;
  1121.             psz_session = httpd_MsgGet( query, "Session" );
  1122.             msg_Dbg( p_vod, "HTTPD_MSG_TEARDOWN for session: %s", psz_session);
  1123.             p_rtsp = RtspClientGet( p_media, psz_session );
  1124.             if( !p_rtsp ) break;
  1125.             CommandPush( p_vod, RTSP_CMD_TYPE_STOP, p_media, psz_session,
  1126.                          0, NULL );
  1127.             RtspClientDel( p_media, p_rtsp );
  1128.             break;
  1129.         case HTTPD_MSG_GETPARAMETER:
  1130.             answer->i_status = 200;
  1131.             answer->i_body = 0;
  1132.             answer->p_body = NULL;
  1133.             break;
  1134.         default:
  1135.             return VLC_EGENERIC;
  1136.     }
  1137.     httpd_MsgAdd( answer, "Server", "VLC Server" );
  1138.     httpd_MsgAdd( answer, "Content-Length", "%d", answer->i_body );
  1139.     psz_cseq = httpd_MsgGet( query, "Cseq" );
  1140.     psz_cseq ? i_cseq = atoi( psz_cseq ) : 0;
  1141.     httpd_MsgAdd( answer, "CSeq", "%d", i_cseq );
  1142.     httpd_MsgAdd( answer, "Cache-Control", "%s", "no-cache" );
  1143.     if( psz_session )
  1144.     {
  1145.          if( p_media->p_vod->p_sys->i_session_timeout >= 0 )
  1146.              httpd_MsgAdd( answer, "Session", "%s;timeout=%i", psz_session,
  1147.                p_media->p_vod->p_sys->i_session_timeout );
  1148.          else
  1149.               httpd_MsgAdd( answer, "Session", "%s", psz_session );
  1150.     }
  1151.     return VLC_SUCCESS;
  1152. }
  1153. static int RtspCallbackES( httpd_callback_sys_t *p_args, httpd_client_t *cl,
  1154.                            httpd_message_t *answer,
  1155.                            const httpd_message_t *query )
  1156. {
  1157.     media_es_t *p_es = (media_es_t*)p_args;
  1158.     vod_media_t *p_media = p_es->p_media;
  1159.     vod_t *p_vod = p_media->p_vod;
  1160.     rtsp_client_t *p_rtsp = NULL;
  1161.     const char *psz_transport = NULL;
  1162.     const char *psz_playnow = NULL; /* support option: x-playNow */
  1163.     const char *psz_session = NULL;
  1164.     const char *psz_position = NULL;
  1165.     const char *psz_cseq = NULL;
  1166.     int i_cseq = 0;
  1167.     int i;
  1168.     if( answer == NULL || query == NULL ) return VLC_SUCCESS;
  1169.     msg_Dbg( p_vod, "RtspCallback query: type=%d", query->i_type );
  1170.     answer->i_proto   = HTTPD_PROTO_RTSP;
  1171.     answer->i_version = query->i_version;
  1172.     answer->i_type    = HTTPD_MSG_ANSWER;
  1173.     answer->i_body    = 0;
  1174.     answer->p_body      = NULL;
  1175.     switch( query->i_type )
  1176.     {
  1177.         case HTTPD_MSG_SETUP:
  1178.             psz_playnow = httpd_MsgGet( query, "x-playNow" );
  1179.             psz_transport = httpd_MsgGet( query, "Transport" );
  1180.             msg_Dbg( p_vod, "HTTPD_MSG_SETUP: transport=%s", psz_transport );
  1181.             if( strstr( psz_transport, "unicast" ) &&
  1182.                 strstr( psz_transport, "client_port=" ) )
  1183.             {
  1184.                 rtsp_client_t *p_rtsp = NULL;
  1185.                 rtsp_client_es_t *p_rtsp_es = NULL;
  1186.                 char ip[NI_MAXNUMERICHOST];
  1187.                 int i_port = atoi( strstr( psz_transport, "client_port=" ) +
  1188.                                    strlen("client_port=") );
  1189.                 if( httpd_ClientIP( cl, ip ) == NULL )
  1190.                 {
  1191.                     answer->i_status = 500;
  1192.                     answer->i_body = 0;
  1193.                     answer->p_body = NULL;
  1194.                     break;
  1195.                 }
  1196.                 msg_Dbg( p_vod, "HTTPD_MSG_SETUP: unicast ip=%s port=%d",
  1197.                         ip, i_port );
  1198.                 psz_session = httpd_MsgGet( query, "Session" );
  1199.                 if( !psz_session || !*psz_session )
  1200.                 {
  1201.                     char *psz_new;
  1202.                     if( ( p_vod->p_sys->i_throttle_users > 0 ) &&
  1203.                         ( p_vod->p_sys->i_connections >= p_vod->p_sys->i_throttle_users ) )
  1204.                     {
  1205.                         answer->i_status = 503;
  1206.                         answer->i_body = 0;
  1207.                         answer->p_body = NULL;
  1208.                         break;
  1209.                     }
  1210.                     if( asprintf( &psz_new, "%d", rand() ) < 0 )
  1211.                         return VLC_ENOMEM;
  1212.                     psz_session = psz_new;
  1213.                     p_rtsp = RtspClientNew( p_media, psz_new );
  1214.                     if( !p_rtsp )
  1215.                     {
  1216.                         answer->i_status = 454;
  1217.                         answer->i_body = 0;
  1218.                         answer->p_body = NULL;
  1219.                         break;
  1220.                     }
  1221.                 }
  1222.                 else
  1223.                 {
  1224.                     p_rtsp = RtspClientGet( p_media, psz_session );
  1225.                     if( !p_rtsp )
  1226.                     {
  1227.                         answer->i_status = 454;
  1228.                         answer->i_body = 0;
  1229.                         answer->p_body = NULL;
  1230.                         break;
  1231.                     }
  1232.                 }
  1233.                 p_rtsp_es = malloc( sizeof(rtsp_client_es_t) );
  1234.                 if( !p_rtsp_es )
  1235.                 {
  1236.                     answer->i_status = 500;
  1237.                     answer->i_body = 0;
  1238.                     answer->p_body = NULL;
  1239.                     break;
  1240.                 }
  1241.                 p_rtsp_es->i_port = i_port;
  1242.                 p_rtsp_es->psz_ip = strdup( ip );
  1243.                 p_rtsp_es->p_media_es = p_es;
  1244.                 TAB_APPEND( p_rtsp->i_es, p_rtsp->es, p_rtsp_es );
  1245.                 answer->i_status = 200;
  1246.                 answer->i_body = 0;
  1247.                 answer->p_body = NULL;
  1248.                 if( p_media->b_raw )
  1249.                 {
  1250.                     if( strstr( psz_transport, "MP2T/H2221/UDP" ) )
  1251.                     {
  1252.                         httpd_MsgAdd( answer, "Transport",
  1253.                                      "MP2T/H2221/UDP;client_port=%d-%d",
  1254.                                      p_rtsp_es->i_port, p_rtsp_es->i_port + 1 );
  1255.                     }
  1256.                     else if( strstr( psz_transport, "RAW/RAW/UDP" ) )
  1257.                     {
  1258.                         httpd_MsgAdd( answer, "Transport",
  1259.                                      "RAW/RAW/UDP;client_port=%d-%d",
  1260.                                      p_rtsp_es->i_port, p_rtsp_es->i_port + 1 );
  1261.                     }
  1262.                 }
  1263.                 else
  1264.                 {
  1265.                     httpd_MsgAdd( answer, "Transport",
  1266.                                   "RTP/AVP/UDP;client_port=%d-%d",
  1267.                                   p_rtsp_es->i_port, p_rtsp_es->i_port + 1 );
  1268.                 }
  1269.             }
  1270.             else /* TODO  strstr( psz_transport, "interleaved" ) ) */
  1271.             {
  1272.                 answer->i_status = 461;
  1273.                 answer->i_body = 0;
  1274.                 answer->p_body = NULL;
  1275.             }
  1276.             /* Intentional fall-through on x-playNow option in RTSP request */
  1277.             if( !psz_playnow )
  1278.                 break;
  1279.         case HTTPD_MSG_PLAY:
  1280.             /* This is kind of a kludge. Should we only support Aggregate
  1281.              * Operations ? */
  1282.             psz_session = httpd_MsgGet( query, "Session" );
  1283.             msg_Dbg( p_vod, "HTTPD_MSG_PLAY for session: %s", psz_session );
  1284.             p_rtsp = RtspClientGet( p_media, psz_session );
  1285.             psz_position = httpd_MsgGet( query, "Range" );
  1286.             if( psz_position ) psz_position = strstr( psz_position, "npt=" );
  1287.             if( psz_position )
  1288.             {
  1289.                 double f_pos = ParseNPT (psz_position + 4);
  1290.                 msg_Dbg( p_vod, "seeking request: %s", psz_position );
  1291.                 f_pos /= ((double)(p_media->i_length))/1000 /1000 / 100;
  1292.                 CommandPush( p_vod, RTSP_CMD_TYPE_SEEK, p_media,
  1293.                              psz_session, f_pos, NULL );
  1294.             }
  1295.             if( !psz_playnow )
  1296.             {
  1297.                 answer->i_status = 200;
  1298.                 answer->i_body = 0;
  1299.                 answer->p_body = NULL;
  1300.             }
  1301.             break;
  1302.         case HTTPD_MSG_TEARDOWN:
  1303.             answer->i_status = 200;
  1304.             answer->i_body = 0;
  1305.             answer->p_body = NULL;
  1306.             psz_session = httpd_MsgGet( query, "Session" );
  1307.             msg_Dbg( p_vod, "HTTPD_MSG_TEARDOWN for session: %s", psz_session);
  1308.             p_rtsp = RtspClientGet( p_media, psz_session );
  1309.             if( !p_rtsp ) break;
  1310.             for( i = 0; i < p_rtsp->i_es; i++ )
  1311.             {
  1312.                 if( p_rtsp->es[i]->p_media_es == p_es )
  1313.                 {
  1314.                     free( p_rtsp->es[i]->psz_ip );
  1315.                     TAB_REMOVE( p_rtsp->i_es, p_rtsp->es, p_rtsp->es[i] );
  1316.                     break;
  1317.                 }
  1318.             }
  1319.             if( !p_rtsp->i_es )
  1320.             {
  1321.                 CommandPush( p_vod, RTSP_CMD_TYPE_STOP, p_media, psz_session,
  1322.                              0, NULL );
  1323.                 RtspClientDel( p_media, p_rtsp );
  1324.             }
  1325.             break;
  1326.         case HTTPD_MSG_PAUSE:
  1327.             /* This is kind of a kludge. Should we only support Aggregate
  1328.              * Operations ? */
  1329.             psz_session = httpd_MsgGet( query, "Session" );
  1330.             msg_Dbg( p_vod, "HTTPD_MSG_PAUSE for session: %s", psz_session );
  1331.             p_rtsp = RtspClientGet( p_media, psz_session );
  1332.             if( !p_rtsp ) break;
  1333.             CommandPush( p_vod, RTSP_CMD_TYPE_PAUSE, p_media, psz_session,
  1334.                          0, NULL );
  1335.             p_rtsp->b_paused = true;
  1336.             answer->i_status = 200;
  1337.             answer->i_body = 0;
  1338.             answer->p_body = NULL;
  1339.             break;
  1340.         default:
  1341.             return VLC_EGENERIC;
  1342.             break;
  1343.     }
  1344.     httpd_MsgAdd( answer, "Server", "VLC Server" );
  1345.     httpd_MsgAdd( answer, "Content-Length", "%d", answer->i_body );
  1346.     psz_cseq = httpd_MsgGet( query, "Cseq" );
  1347.     if (psz_cseq)
  1348.         i_cseq = atoi( psz_cseq );
  1349.     else
  1350.         i_cseq = 0;
  1351.     httpd_MsgAdd( answer, "Cseq", "%d", i_cseq );
  1352.     httpd_MsgAdd( answer, "Cache-Control", "%s", "no-cache" );
  1353.     if( psz_session )
  1354.         httpd_MsgAdd( answer, "Session", "%s"/*;timeout=5*/, psz_session );
  1355.     return VLC_SUCCESS;
  1356. }
  1357. /*****************************************************************************
  1358.  * SDPGenerate: TODO
  1359.  * FIXME: need to be moved to a common place ?
  1360.  *****************************************************************************/
  1361. static char *SDPGenerate( const vod_media_t *p_media, httpd_client_t *cl )
  1362. {
  1363.     int i, i_size;
  1364.     char *p, *psz_sdp, ip[NI_MAXNUMERICHOST], ipv;
  1365.     const char *psz_control;
  1366.     if( httpd_ServerIP( cl, ip ) == NULL )
  1367.         return NULL;
  1368.     p = strchr( ip, '%' );
  1369.     if( p != NULL )
  1370.         *p = ''; /* remove scope if present */
  1371.     ipv = ( strchr( ip, ':' ) != NULL ) ? '6' : '4';
  1372.     /* Calculate size */
  1373.     i_size = sizeof( "v=0rn" ) +
  1374.         sizeof( "o=- * * IN IP4 rn" ) + 10 + NI_MAXNUMERICHOST +
  1375.         sizeof( "s=*rn" ) + strlen( p_media->psz_session_name ) +
  1376.         sizeof( "i=*rn" ) + strlen( p_media->psz_session_description ) +
  1377.         sizeof( "u=*rn" ) + strlen( p_media->psz_session_url ) +
  1378.         sizeof( "e=*rn" ) + strlen( p_media->psz_session_email ) +
  1379.         sizeof( "c=IN IP4 0.0.0.0rn" ) + 20 + 10 +
  1380.         sizeof( "t=0 0rn" ) + /* FIXME */
  1381.         sizeof( "a=tool:"PACKAGE_STRING"rn" ) +
  1382.         sizeof( "a=range:npt=0-1000000000.000rn" );
  1383.     psz_control = (ipv == '6') ? p_media->psz_rtsp_control_v6
  1384.                                : p_media->psz_rtsp_control_v4;
  1385.     for( i = 0; i < p_media->i_es; i++ )
  1386.     {
  1387.         media_es_t *p_es = p_media->es[i];
  1388.         i_size += sizeof( "m=**d*o * RTP/AVP *rn" ) + 19;
  1389.         if( p_es->psz_rtpmap )
  1390.         {
  1391.             i_size += sizeof( "a=rtpmap:* *rn" ) +
  1392.                 strlen( p_es->psz_rtpmap ) + 9;
  1393.         }
  1394.         if( p_es->psz_fmtp )
  1395.         {
  1396.             i_size += sizeof( "a=fmtp:* *rn" ) +
  1397.                 strlen( p_es->psz_fmtp ) + 9;
  1398.         }
  1399.     }
  1400.     i_size += (strlen( psz_control ) + strlen( ip ) + 9) * p_media->i_es;
  1401.     p = psz_sdp = malloc( i_size );
  1402.     p += sprintf( p, "v=0rn" );
  1403.     p += sprintf( p, "o=- %"PRId64" %d IN IP%c %srn",
  1404.                   p_media->i_sdp_id, p_media->i_sdp_version, ipv, ip );
  1405.     if( *p_media->psz_session_name )
  1406.         p += sprintf( p, "s=%srn", p_media->psz_session_name );
  1407.     if( *p_media->psz_session_description )
  1408.         p += sprintf( p, "i=%srn", p_media->psz_session_description );
  1409.     if( *p_media->psz_session_url )
  1410.         p += sprintf( p, "u=%srn", p_media->psz_session_url );
  1411.     if( *p_media->psz_session_email )
  1412.         p += sprintf( p, "e=%srn", p_media->psz_session_email );
  1413.     p += sprintf( p, "c=IN IP%c %srn", ipv, ipv == '6' ? "::" : "0.0.0.0" );
  1414.     p += sprintf( p, "t=0 0rn" ); /* FIXME */
  1415.     p += sprintf( p, "a=tool:"PACKAGE_STRING"rn" );
  1416.     if( p_media->i_length > 0 )
  1417.     {
  1418.         lldiv_t d = lldiv( p_media->i_length / 1000, 1000 );
  1419.         p += sprintf( p, "a=range:npt=0-%lld.%03urn", d.quot,
  1420.                       (unsigned)d.rem );
  1421.     }
  1422.     for( i = 0; i < p_media->i_es; i++ )
  1423.     {
  1424.         media_es_t *p_es = p_media->es[i];
  1425.         if( p_es->fmt.i_cat == AUDIO_ES )
  1426.         {
  1427.             p += sprintf( p, "m=audio %d RTP/AVP %drn",
  1428.                           p_es->i_port, p_es->i_payload_type );
  1429.         }
  1430.         else if( p_es->fmt.i_cat == VIDEO_ES )
  1431.         {
  1432.             p += sprintf( p, "m=video %d RTP/AVP %drn",
  1433.                           p_es->i_port, p_es->i_payload_type );
  1434.         }
  1435.         else
  1436.         {
  1437.             continue;
  1438.         }
  1439.         if( p_es->psz_rtpmap )
  1440.         {
  1441.             p += sprintf( p, "a=rtpmap:%d %srn", p_es->i_payload_type,
  1442.                           p_es->psz_rtpmap );
  1443.         }
  1444.         if( p_es->psz_fmtp )
  1445.         {
  1446.             p += sprintf( p, "a=fmtp:%d %srn", p_es->i_payload_type,
  1447.                           p_es->psz_fmtp );
  1448.         }
  1449.         p += sprintf( p, psz_control, ip, i );
  1450.     }
  1451.     return psz_sdp;
  1452. }