rtsp.c
上传用户:riyaled888
上传日期:2009-03-27
资源大小:7338k
文件大小:31k
源码类别:

多媒体

开发平台:

MultiPlatform

  1. /*****************************************************************************
  2.  * rtsp.c: rtsp VoD server module
  3.  *****************************************************************************
  4.  * Copyright (C) 2003-2004 VideoLAN
  5.  * $Id: rtsp.c 9020 2004-10-20 12:01:09Z gbazin $
  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., 59 Temple Place - Suite 330, Boston, MA  02111, USA.
  23.  *****************************************************************************/
  24. /*****************************************************************************
  25.  * Preamble
  26.  *****************************************************************************/
  27. #include <stdlib.h>
  28. #include <errno.h>
  29. #include <vlc/vlc.h>
  30. #include <vlc/input.h>
  31. #include <vlc/sout.h>
  32. #include "vlc_httpd.h"
  33. #include "vlc_vod.h"
  34. #include "network.h"
  35. /*****************************************************************************
  36.  * Module descriptor
  37.  *****************************************************************************/
  38. static int  Open ( vlc_object_t * );
  39. static void Close( vlc_object_t * );
  40. #define HOST_TEXT N_( "Host address" )
  41. #define HOST_LONGTEXT N_( 
  42.     "You can set the address, port and path the rtsp interface will bind to." )
  43. vlc_module_begin();
  44.     set_description( _("RTSP VoD server") );
  45.     set_capability( "vod server", 1 );
  46.     set_callbacks( Open, Close );
  47.     add_shortcut( "rtsp" );
  48.     add_string ( "rtsp-host", NULL, NULL, HOST_TEXT, HOST_LONGTEXT, VLC_TRUE );
  49. vlc_module_end();
  50. /*****************************************************************************
  51.  * Exported prototypes
  52.  *****************************************************************************/
  53. typedef struct media_es_t media_es_t;
  54. typedef struct
  55. {
  56.     media_es_t *p_media_es;
  57.     char *psz_ip;
  58.     int i_port;
  59. } rtsp_client_es_t;
  60. typedef struct
  61. {
  62.     char *psz_session;
  63.     int64_t i_last; /* for timeout */
  64.     vlc_bool_t b_playing; /* is it in "play" state */
  65.     vlc_bool_t b_paused; /* is it in "pause" state */
  66.     int i_es;
  67.     rtsp_client_es_t **es;
  68. } rtsp_client_t;
  69. struct media_es_t
  70. {
  71.     /* VoD server */
  72.     vod_t *p_vod;
  73.     /* RTSP server */
  74.     httpd_url_t *p_rtsp_url;
  75.     vod_media_t *p_media;
  76.     es_format_t fmt;
  77.     int         i_port;
  78.     uint8_t     i_payload_type;
  79.     char        *psz_rtpmap;
  80.     char        *psz_fmtp;
  81. };
  82. struct vod_media_t
  83. {
  84.     /* VoD server */
  85.     vod_t *p_vod;
  86.     /* RTSP server */
  87.     httpd_url_t  *p_rtsp_url;
  88.     char         *psz_rtsp_control;
  89.     char         *psz_rtsp_path;
  90.     char *psz_destination;
  91.     int  i_port;
  92.     int  i_port_audio;
  93.     int  i_port_video;
  94.     int  i_ttl;
  95.     int  i_payload_type;
  96.     int64_t i_sdp_id;
  97.     int     i_sdp_version;
  98.     vlc_bool_t b_multicast;
  99.     vlc_mutex_t lock;
  100.     /* ES list */
  101.     int        i_es;
  102.     media_es_t **es;
  103.     char       *psz_mux;
  104.     /* RTSP client */
  105.     int           i_rtsp;
  106.     rtsp_client_t **rtsp;
  107.     /* Infos */
  108.     char *psz_session_name;
  109.     char *psz_session_description;
  110.     char *psz_session_url;
  111.     char *psz_session_email;
  112. };
  113. struct vod_sys_t
  114. {
  115.     /* RTSP server */
  116.     httpd_host_t *p_rtsp_host;
  117.     char *psz_host;
  118.     char *psz_path;
  119.     int i_port;
  120.     /* List of media */
  121.     int i_media;
  122.     vod_media_t **media;
  123. };
  124. static vod_media_t *MediaNew( vod_t *, char *, input_item_t * );
  125. static void         MediaDel( vod_t *, vod_media_t * );
  126. static int          MediaAddES( vod_t *, vod_media_t *, es_format_t * );
  127. static void         MediaDelES( vod_t *, vod_media_t *, es_format_t * );
  128. static rtsp_client_t *RtspClientNew( vod_media_t *, char * );
  129. static rtsp_client_t *RtspClientGet( vod_media_t *, char * );
  130. static void           RtspClientDel( vod_media_t *, rtsp_client_t * );
  131. static int RtspCallback( httpd_callback_sys_t *, httpd_client_t *,
  132.                          httpd_message_t *, httpd_message_t * );
  133. static int RtspCallbackES( httpd_callback_sys_t *, httpd_client_t *,
  134.                            httpd_message_t *, httpd_message_t * );
  135. static char *SDPGenerate( vod_media_t *, char * );
  136. static void sprintf_hexa( char *s, uint8_t *p_data, int i_data )
  137. {
  138.     static const char hex[16] = "0123456789abcdef";
  139.     int i;
  140.     for( i = 0; i < i_data; i++ )
  141.     {
  142.         s[2*i+0] = hex[(p_data[i]>>4)&0xf];
  143.         s[2*i+1] = hex[(p_data[i]   )&0xf];
  144.     }
  145.     s[2*i_data] = '';
  146. }
  147. /*****************************************************************************
  148.  * Open: Starts the RTSP server module
  149.  *****************************************************************************/
  150. static int Open( vlc_object_t *p_this )
  151. {
  152.     vod_t *p_vod = (vod_t *)p_this;
  153.     vod_sys_t *p_sys = 0;
  154.     char *psz_url = 0;
  155.     vlc_url_t url;
  156.     psz_url = config_GetPsz( p_vod, "rtsp-host" );
  157.     vlc_UrlParse( &url, psz_url, 0 );
  158.     if( psz_url ) free( psz_url );
  159.     if( !url.psz_host || !*url.psz_host )
  160.     {
  161.         if( url.psz_host ) free( url.psz_host );
  162.         url.psz_host = strdup( "localhost" );
  163.     }
  164.     if( url.i_port <= 0 ) url.i_port = 554;
  165.     p_vod->p_sys = p_sys = malloc( sizeof( vod_sys_t ) );
  166.     if( !p_sys ) goto error;
  167.     p_sys->p_rtsp_host = 0;
  168.     p_sys->p_rtsp_host =
  169.         httpd_HostNew( VLC_OBJECT(p_vod), url.psz_host, url.i_port );
  170.     if( !p_sys->p_rtsp_host )
  171.     {
  172.         msg_Err( p_vod, "cannot create http server (%s:%i)",
  173.                  url.psz_host, url.i_port );
  174.         goto error;
  175.     }
  176.     p_sys->psz_host = strdup( url.psz_host );
  177.     p_sys->psz_path = strdup( url.psz_path ? url.psz_path : "/" );
  178.     p_sys->i_port = url.i_port;
  179.     vlc_UrlClean( &url );
  180.     p_sys->media = 0;
  181.     p_sys->i_media = 0;
  182.     p_vod->pf_media_new = MediaNew;
  183.     p_vod->pf_media_del = MediaDel;
  184.     p_vod->pf_media_add_es = MediaAddES;
  185.     p_vod->pf_media_del_es = MediaDelES;
  186.     return VLC_SUCCESS;
  187.  error:
  188.     if( p_sys && p_sys->p_rtsp_host ) httpd_HostDelete( p_sys->p_rtsp_host );
  189.     if( p_sys ) free( p_sys );
  190.     vlc_UrlClean( &url );
  191.     return VLC_EGENERIC;
  192. }
  193. /*****************************************************************************
  194.  * Close:
  195.  *****************************************************************************/
  196. static void Close( vlc_object_t * p_this )
  197. {
  198.     vod_t *p_vod = (vod_t *)p_this;
  199.     vod_sys_t *p_sys = p_vod->p_sys;
  200.     httpd_HostDelete( p_sys->p_rtsp_host );
  201.     /* TODO delete medias */
  202.     free( p_sys );
  203. }
  204. /*****************************************************************************
  205.  * Media handling
  206.  *****************************************************************************/
  207. static vod_media_t *MediaNew( vod_t *p_vod, char *psz_name,
  208.                               input_item_t *p_item )
  209. {
  210.     vod_sys_t *p_sys = p_vod->p_sys;
  211.     vod_media_t *p_media = malloc( sizeof(vod_media_t) );
  212.     int i;
  213.     memset( p_media, 0, sizeof(vod_media_t) );
  214.     p_media->es = 0;
  215.     p_media->psz_mux = 0;
  216.     p_media->rtsp = 0;
  217.     asprintf( &p_media->psz_rtsp_path, "%s%s", p_sys->psz_path, psz_name );
  218.     p_media->p_rtsp_url =
  219.         httpd_UrlNewUnique( p_sys->p_rtsp_host, p_media->psz_rtsp_path, 0, 0 );
  220.     if( !p_media->p_rtsp_url )
  221.     {
  222.         msg_Err( p_vod, "cannot create http url (%s)", p_media->psz_rtsp_path);
  223.         free( p_media->psz_rtsp_path );
  224.         free( p_media );
  225.         return 0;
  226.     }
  227.     msg_Dbg( p_vod, "created rtsp url: %s", p_media->psz_rtsp_path );
  228.     asprintf( &p_media->psz_rtsp_control, "rtsp://%s:%d%s",
  229.               p_sys->psz_host, p_sys->i_port, p_media->psz_rtsp_path );
  230.     httpd_UrlCatch( p_media->p_rtsp_url, HTTPD_MSG_DESCRIBE,
  231.                     RtspCallback, (void*)p_media );
  232.     httpd_UrlCatch( p_media->p_rtsp_url, HTTPD_MSG_PLAY,
  233.                     RtspCallback, (void*)p_media );
  234.     httpd_UrlCatch( p_media->p_rtsp_url, HTTPD_MSG_PAUSE,
  235.                     RtspCallback, (void*)p_media );
  236.     httpd_UrlCatch( p_media->p_rtsp_url, HTTPD_MSG_TEARDOWN,
  237.                     RtspCallback, (void*)p_media );
  238.     p_media->p_vod = p_vod;
  239.     TAB_APPEND( p_sys->i_media, p_sys->media, p_media );
  240.     vlc_mutex_init( p_vod, &p_media->lock );
  241.     p_media->psz_session_name = strdup("");
  242.     p_media->psz_session_description = strdup("");
  243.     p_media->psz_session_url = strdup("");
  244.     p_media->psz_session_email = strdup("");
  245.     p_media->i_port_audio = 1234;
  246.     p_media->i_port_video = 1236;
  247.     p_media->i_port       = 1238;
  248.     p_media->i_payload_type = 96;
  249.     p_media->i_sdp_id = mdate();
  250.     p_media->i_sdp_version = 1;
  251.     vlc_mutex_lock( &p_item->lock );
  252.     msg_Dbg( p_vod, "media has %i declared ES", p_item->i_es );
  253.     for( i = 0; i < p_item->i_es; i++ )
  254.     {
  255.         MediaAddES( p_vod, p_media, p_item->es[i] );
  256.     }
  257.     vlc_mutex_unlock( &p_item->lock );
  258.     return p_media;
  259. }
  260. static void MediaDel( vod_t *p_vod, vod_media_t *p_media )
  261. {
  262.     vod_sys_t *p_sys = p_vod->p_sys;
  263.     msg_Dbg( p_vod, "deleting media: %s", p_media->psz_rtsp_path );
  264.     while( p_media->i_rtsp > 0 ) RtspClientDel( p_media, p_media->rtsp[0] );
  265.     httpd_UrlDelete( p_media->p_rtsp_url );
  266.     if( p_media->psz_rtsp_path ) free( p_media->psz_rtsp_path );
  267.     if( p_media->psz_rtsp_control ) free( p_media->psz_rtsp_control );
  268.     TAB_REMOVE( p_sys->i_media, p_sys->media, p_media );
  269.     while( p_media->i_es ) MediaDelES( p_vod, p_media, &p_media->es[0]->fmt );
  270.     vlc_mutex_destroy( &p_media->lock );
  271.     free( p_media );
  272. }
  273. static int MediaAddES( vod_t *p_vod, vod_media_t *p_media, es_format_t *p_fmt )
  274. {
  275.     media_es_t *p_es = malloc( sizeof(media_es_t) );
  276.     char *psz_urlc;
  277.     memset( p_es, 0, sizeof(media_es_t) );
  278.     p_media->psz_mux = NULL;
  279.     /* TODO: update SDP, etc... */
  280.     asprintf( &psz_urlc, "%s/trackid=%d",
  281.               p_media->psz_rtsp_path, p_media->i_es );
  282.     msg_Dbg( p_vod, "  - ES %4.4s (%s)", (char *)&p_fmt->i_codec, psz_urlc );
  283.     switch( p_fmt->i_codec )
  284.     {
  285.     case VLC_FOURCC( 's', '1', '6', 'b' ):
  286.         if( p_fmt->audio.i_channels == 1 && p_fmt->audio.i_rate == 44100 )
  287.         {
  288.             p_es->i_payload_type = 11;
  289.         }
  290.         else if( p_fmt->audio.i_channels == 2 && p_fmt->audio.i_rate == 44100 )
  291.         {
  292.             p_es->i_payload_type = 10;
  293.         }
  294.         else
  295.         {
  296.             p_es->i_payload_type = p_media->i_payload_type++;
  297.         }
  298.         p_es->psz_rtpmap = malloc( strlen( "L16/*/*" ) + 20+1 );
  299.         sprintf( p_es->psz_rtpmap, "L16/%d/%d", p_fmt->audio.i_rate,
  300.                  p_fmt->audio.i_channels );
  301.         break;
  302.     case VLC_FOURCC( 'u', '8', ' ', ' ' ):
  303.         p_es->i_payload_type = p_media->i_payload_type++;
  304.         p_es->psz_rtpmap = malloc( strlen( "L8/*/*" ) + 20+1 );
  305.         sprintf( p_es->psz_rtpmap, "L8/%d/%d", p_fmt->audio.i_rate,
  306.                  p_fmt->audio.i_channels );
  307.         break;
  308.     case VLC_FOURCC( 'm', 'p', 'g', 'a' ):
  309.         p_es->i_payload_type = 14;
  310.         p_es->psz_rtpmap = strdup( "MPA/90000" );
  311.         break;
  312.     case VLC_FOURCC( 'm', 'p', 'g', 'v' ):
  313.         p_es->i_payload_type = 32;
  314.         p_es->psz_rtpmap = strdup( "MPV/90000" );
  315.         break;
  316.     case VLC_FOURCC( 'a', '5', '2', ' ' ):
  317.         p_es->i_payload_type = p_media->i_payload_type++;
  318.         p_es->psz_rtpmap = strdup( "ac3/90000" );
  319.         break;
  320.     case VLC_FOURCC( 'H', '2', '6', '3' ):
  321.         p_es->i_payload_type = p_media->i_payload_type++;
  322.         p_es->psz_rtpmap = strdup( "H263-1998/90000" );
  323.         break;
  324.     case VLC_FOURCC( 'm', 'p', '4', 'v' ):
  325.         p_es->i_payload_type = p_media->i_payload_type++;
  326.         p_es->psz_rtpmap = strdup( "MP4V-ES/90000" );
  327.         if( p_fmt->i_extra > 0 )
  328.         {
  329.             char *p_hexa = malloc( 2 * p_fmt->i_extra + 1 );
  330.             p_es->psz_fmtp = malloc( 100 + 2 * p_fmt->i_extra );
  331.             sprintf_hexa( p_hexa, p_fmt->p_extra, p_fmt->i_extra );
  332.             sprintf( p_es->psz_fmtp,
  333.                      "profile-level-id=3; config=%s;", p_hexa );
  334.             free( p_hexa );
  335.         }
  336.         break;
  337.     case VLC_FOURCC( 'm', 'p', '4', 'a' ):
  338.         p_es->i_payload_type = p_media->i_payload_type++;
  339.         p_es->psz_rtpmap = malloc( strlen( "mpeg4-generic/" ) + 12 );
  340.         sprintf( p_es->psz_rtpmap, "mpeg4-generic/%d", p_fmt->audio.i_rate );
  341.         if( p_fmt->i_extra > 0 )
  342.         {
  343.             char *p_hexa = malloc( 2 * p_fmt->i_extra + 1 );
  344.             p_es->psz_fmtp = malloc( 200 + 2 * p_fmt->i_extra );
  345.             sprintf_hexa( p_hexa, p_fmt->p_extra, p_fmt->i_extra );
  346.             sprintf( p_es->psz_fmtp,
  347.                      "streamtype=5; profile-level-id=15; mode=AAC-hbr; "
  348.                      "config=%s; SizeLength=13;IndexLength=3; "
  349.                      "IndexDeltaLength=3; Profile=1;", p_hexa );
  350.             free( p_hexa );
  351.         }
  352.         break;
  353.     case VLC_FOURCC( 'm', 'p', '2', 't' ):
  354.         p_media->psz_mux = "ts";
  355.         p_es->i_payload_type = 33;
  356.         p_es->psz_rtpmap = strdup( "MP2T/90000" );
  357.         break;
  358.     case VLC_FOURCC( 'm', 'p', '2', 'p' ):
  359.         p_media->psz_mux = "ps";
  360.         p_es->i_payload_type = p_media->i_payload_type++;
  361.         p_es->psz_rtpmap = strdup( "MP2P/90000" );
  362.         break;
  363.     default:
  364.         msg_Err( p_vod, "cannot add this stream (unsupported "
  365.                  "codec: %4.4s)", (char*)&p_fmt->i_codec );
  366.         free( p_es );
  367.         return VLC_EGENERIC;
  368.     }
  369.     p_es->p_rtsp_url =
  370.         httpd_UrlNewUnique( p_vod->p_sys->p_rtsp_host, psz_urlc, 0, 0 );
  371.     if( !p_es->p_rtsp_url )
  372.     {
  373.         msg_Err( p_vod, "cannot create http url (%s)", psz_urlc );
  374.         free( psz_urlc );
  375.         free( p_es );
  376.         return VLC_EGENERIC;
  377.     }
  378.     free( psz_urlc );
  379.     httpd_UrlCatch( p_es->p_rtsp_url, HTTPD_MSG_SETUP,
  380.                     RtspCallbackES, (void*)p_es );
  381.     httpd_UrlCatch( p_es->p_rtsp_url, HTTPD_MSG_TEARDOWN,
  382.                     RtspCallbackES, (void*)p_es );
  383.     httpd_UrlCatch( p_es->p_rtsp_url, HTTPD_MSG_PLAY,
  384.                     RtspCallbackES, (void*)p_es );
  385.     httpd_UrlCatch( p_es->p_rtsp_url, HTTPD_MSG_PAUSE,
  386.                     RtspCallbackES, (void*)p_es );
  387.     es_format_Copy( &p_es->fmt, p_fmt );
  388.     p_es->p_vod = p_vod;
  389.     p_es->p_media = p_media;
  390. #if 0
  391.     /* Choose the port */
  392.     if( p_fmt->i_cat == AUDIO_ES && p_media->i_port_audio > 0 )
  393.     {
  394.         p_es->i_port = p_media->i_port_audio;
  395.         p_media->i_port_audio = 0;
  396.     }
  397.     else if( p_fmt->i_cat == VIDEO_ES && p_media->i_port_video > 0 )
  398.     {
  399.         p_es->i_port = p_media->i_port_video;
  400.         p_media->i_port_video = 0;
  401.     }
  402.     while( !p_es->i_port )
  403.     {
  404.         if( p_media->i_port != p_media->i_port_audio &&
  405.             p_media->i_port != p_media->i_port_video )
  406.         {
  407.             p_es->i_port = p_media->i_port;
  408.             p_media->i_port += 2;
  409.             break;
  410.         }
  411.         p_media->i_port += 2;
  412.     }
  413. #else
  414.     p_es->i_port = 0;
  415. #endif
  416.     vlc_mutex_lock( &p_media->lock );
  417.     TAB_APPEND( p_media->i_es, p_media->es, p_es );
  418.     vlc_mutex_unlock( &p_media->lock );
  419.     p_media->i_sdp_version++;
  420.     return VLC_SUCCESS;
  421. }
  422. static void MediaDelES( vod_t *p_vod, vod_media_t *p_media, es_format_t *p_fmt)
  423. {
  424.     media_es_t *p_es = 0;
  425.     int i;
  426.     /* Find the ES */
  427.     for( i = 0; i < p_media->i_es; i++ )
  428.     {
  429.         if( p_media->es[i]->fmt.i_cat == p_fmt->i_cat &&
  430.             p_media->es[i]->fmt.i_codec == p_fmt->i_codec &&
  431.             p_media->es[i]->fmt.i_id == p_fmt->i_id )
  432.         {
  433.             p_es = p_media->es[i];
  434.         }
  435.     }
  436.     if( !p_es ) return;
  437.     msg_Dbg( p_vod, "  - Removing ES %4.4s", (char *)&p_fmt->i_codec );
  438.     vlc_mutex_lock( &p_media->lock );
  439.     TAB_REMOVE( p_media->i_es, p_media->es, p_es );
  440.     vlc_mutex_unlock( &p_media->lock );
  441.     if( p_es->psz_rtpmap ) free( p_es->psz_rtpmap );
  442.     if( p_es->psz_fmtp ) free( p_es->psz_fmtp );
  443.     p_media->i_sdp_version++;
  444.     if( p_es->p_rtsp_url ) httpd_UrlDelete( p_es->p_rtsp_url );
  445.     es_format_Clean( &p_es->fmt );
  446. }
  447. /****************************************************************************
  448.  * RTSP server implementation
  449.  ****************************************************************************/
  450. static rtsp_client_t *RtspClientNew( vod_media_t *p_media, char *psz_session )
  451. {
  452.     rtsp_client_t *p_rtsp = malloc( sizeof(rtsp_client_t) );
  453.     memset( p_rtsp, 0, sizeof(rtsp_client_t) );
  454.     p_rtsp->es = 0;
  455.     p_rtsp->psz_session = psz_session;
  456.     TAB_APPEND( p_media->i_rtsp, p_media->rtsp, p_rtsp );
  457.     msg_Dbg( p_media->p_vod, "new session: %s", psz_session );
  458.     return p_rtsp;
  459. }
  460. static rtsp_client_t *RtspClientGet( vod_media_t *p_media, char *psz_session )
  461. {
  462.     int i;
  463.     for( i = 0; psz_session && i < p_media->i_rtsp; i++ )
  464.     {
  465.         if( !strcmp( p_media->rtsp[i]->psz_session, psz_session ) )
  466.         {
  467.             return p_media->rtsp[i];
  468.         }
  469.     }
  470.     return NULL;
  471. }
  472. static void RtspClientDel( vod_media_t *p_media, rtsp_client_t *p_rtsp )
  473. {
  474.     msg_Dbg( p_media->p_vod, "closing session: %s", p_rtsp->psz_session );
  475.     while( p_rtsp->i_es-- )
  476.     {
  477.         if( p_rtsp->es[p_rtsp->i_es]->psz_ip )
  478.             free( p_rtsp->es[p_rtsp->i_es]->psz_ip );
  479.         free( p_rtsp->es[p_rtsp->i_es] );
  480.         if( !p_rtsp->i_es ) free( p_rtsp->es );
  481.     }
  482.     TAB_REMOVE( p_media->i_rtsp, p_media->rtsp, p_rtsp );
  483.     free( p_rtsp->psz_session );
  484.     free( p_rtsp );
  485. }
  486. static int RtspCallback( httpd_callback_sys_t *p_args, httpd_client_t *cl,
  487.                          httpd_message_t *answer, httpd_message_t *query )
  488. {
  489.     vod_media_t *p_media = (vod_media_t*)p_args;
  490.     vod_t *p_vod = p_media->p_vod;
  491.     char *psz_destination = p_media->psz_destination;
  492.     char *psz_session = NULL;
  493.     rtsp_client_t *p_rtsp;
  494.     if( answer == NULL || query == NULL ) return VLC_SUCCESS;
  495.     fprintf( stderr, "RtspCallback query: type=%dn", query->i_type );
  496.     answer->i_proto   = HTTPD_PROTO_RTSP;
  497.     answer->i_version = query->i_version;
  498.     answer->i_type    = HTTPD_MSG_ANSWER;
  499.     switch( query->i_type )
  500.     {
  501.         case HTTPD_MSG_DESCRIBE:
  502.         {
  503.             char *psz_sdp =
  504.                 SDPGenerate( p_media, psz_destination ?
  505.                              psz_destination : "0.0.0.0" );
  506.             answer->i_status = 200;
  507.             answer->psz_status = strdup( "OK" );
  508.             httpd_MsgAdd( answer, "Content-type",  "%s", "application/sdp" );
  509.             answer->p_body = psz_sdp;
  510.             answer->i_body = strlen( psz_sdp );
  511.             break;
  512.         }
  513.         case HTTPD_MSG_PLAY:
  514.         {
  515.             char *psz_output, *ip;
  516.             int i, i_port_audio = 0, i_port_video = 0;
  517.             /* for now only multicast so easy */
  518.             answer->i_status = 200;
  519.             answer->psz_status = strdup( "OK" );
  520.             answer->i_body = 0;
  521.             answer->p_body = NULL;
  522.             psz_session = httpd_MsgGet( query, "Session" );
  523.             msg_Dbg( p_vod, "HTTPD_MSG_PLAY for session: %s", psz_session );
  524.             p_rtsp = RtspClientGet( p_media, psz_session );
  525.             if( !p_rtsp ) break;
  526.             if( p_rtsp->b_playing && p_rtsp->b_paused )
  527.             {
  528.                 vod_MediaControl( p_vod, p_media, psz_session,
  529.                                   VOD_MEDIA_PAUSE );
  530.                 p_rtsp->b_paused = VLC_FALSE;
  531.                 break;
  532.             }
  533.             else if( p_rtsp->b_playing ) break;
  534.             if( !(ip = httpd_ClientIP( cl )) ) break;
  535.             p_rtsp->b_playing = VLC_TRUE;
  536.             /* FIXME for != 1 video and 1 audio */
  537.             for( i = 0; i < p_rtsp->i_es; i++ )
  538.             {
  539.                 if( p_rtsp->es[i]->p_media_es->fmt.i_cat == AUDIO_ES )
  540.                     i_port_audio = p_rtsp->es[i]->i_port;
  541.                 if( p_rtsp->es[i]->p_media_es->fmt.i_cat == VIDEO_ES )
  542.                     i_port_video = p_rtsp->es[i]->i_port;
  543.             }
  544.             if( p_media->psz_mux )
  545.             {
  546.                 asprintf( &psz_output, "rtp{dst=%s,port=%i,mux=%s}",
  547.                           ip, i_port_video, p_media->psz_mux );
  548.             }
  549.             else
  550.             {
  551.                 asprintf( &psz_output, "rtp{dst=%s,port-video=%i,"
  552.                           "port-audio=%i}", ip, i_port_video, i_port_audio );
  553.             }
  554.             vod_MediaControl( p_vod, p_media, psz_session, VOD_MEDIA_PLAY,
  555.                               psz_output );
  556.             free( psz_output );
  557.             free( ip );
  558.             break;
  559.         }
  560.         case HTTPD_MSG_PAUSE:
  561.             psz_session = httpd_MsgGet( query, "Session" );
  562.             msg_Dbg( p_vod, "HTTPD_MSG_PAUSE for session: %s", psz_session );
  563.             p_rtsp = RtspClientGet( p_media, psz_session );
  564.             if( !p_rtsp ) break;
  565.             vod_MediaControl( p_vod, p_media, psz_session, VOD_MEDIA_PAUSE );
  566.             p_rtsp->b_paused = VLC_TRUE;
  567.             answer->i_status = 200;
  568.             answer->psz_status = strdup( "OK" );
  569.             answer->i_body = 0;
  570.             answer->p_body = NULL;
  571.             break;
  572.         case HTTPD_MSG_TEARDOWN:
  573.             /* for now only multicast so easy again */
  574.             answer->i_status = 200;
  575.             answer->psz_status = strdup( "OK" );
  576.             answer->i_body = 0;
  577.             answer->p_body = NULL;
  578.             psz_session = httpd_MsgGet( query, "Session" );
  579.             msg_Dbg( p_vod, "HTTPD_MSG_TEARDOWN for session: %s", psz_session);
  580.             p_rtsp = RtspClientGet( p_media, psz_session );
  581.             if( !p_rtsp ) break;
  582.             vod_MediaControl( p_vod, p_media, psz_session, VOD_MEDIA_STOP );
  583.             RtspClientDel( p_media, p_rtsp );
  584.             break;
  585.         default:
  586.             return VLC_EGENERIC;
  587.     }
  588.     httpd_MsgAdd( answer, "Server", "VLC Server" );
  589.     httpd_MsgAdd( answer, "Content-Length", "%d", answer->i_body );
  590.     httpd_MsgAdd( answer, "Cseq", "%d",
  591.                   atoi( httpd_MsgGet( query, "Cseq" ) ) );
  592.     httpd_MsgAdd( answer, "Cache-Control", "%s", "no-cache" );
  593.     if( psz_session )
  594.     {
  595.         httpd_MsgAdd( answer, "Session", "%s;timeout=5", psz_session );
  596.     }
  597.     return VLC_SUCCESS;
  598. }
  599. static int RtspCallbackES( httpd_callback_sys_t *p_args, httpd_client_t *cl,
  600.                            httpd_message_t *answer, httpd_message_t *query )
  601. {
  602.     media_es_t *p_es = (media_es_t*)p_args;
  603.     vod_media_t *p_media = p_es->p_media;
  604.     vod_t *p_vod = p_media->p_vod;
  605.     char *psz_session = NULL;
  606.     char *psz_transport = NULL;
  607.     int i;
  608.     if( answer == NULL || query == NULL ) return VLC_SUCCESS;
  609.     fprintf( stderr, "RtspCallback query: type=%dn", query->i_type );
  610.     answer->i_proto   = HTTPD_PROTO_RTSP;
  611.     answer->i_version = query->i_version;
  612.     answer->i_type    = HTTPD_MSG_ANSWER;
  613.     switch( query->i_type )
  614.     {
  615.     case HTTPD_MSG_SETUP:
  616.         psz_transport = httpd_MsgGet( query, "Transport" );
  617.         fprintf( stderr, "HTTPD_MSG_SETUP: transport=%sn", psz_transport );
  618.         if( strstr( psz_transport, "multicast" ) && p_media->psz_destination )
  619.         {
  620.             fprintf( stderr, "HTTPD_MSG_SETUP: multicastn" );
  621.             answer->i_status = 200;
  622.             answer->psz_status = strdup( "OK" );
  623.             answer->i_body = 0;
  624.             answer->p_body = NULL;
  625.             psz_session = httpd_MsgGet( query, "Session" );
  626.             if( !psz_session || !*psz_session )
  627.             {
  628.                 asprintf( &psz_session, "%d", rand() );
  629.             }
  630.             httpd_MsgAdd( answer, "Transport",
  631.                           "RTP/AVP/UDP;destination=%s;port=%d-%d;ttl=%d",
  632.                           p_media->psz_destination, p_media->i_port,
  633.                           p_media->i_port+1, p_media->i_ttl );
  634.         }
  635.         else if( strstr( psz_transport, "unicast" ) &&
  636.                  strstr( psz_transport, "client_port=" ) )
  637.         {
  638.             rtsp_client_t *p_rtsp;
  639.             rtsp_client_es_t *p_rtsp_es;
  640.             char *ip = httpd_ClientIP( cl );
  641.             int i_port = atoi( strstr( psz_transport, "client_port=" ) +
  642.                                strlen("client_port=") );
  643.             if( !ip )
  644.             {
  645.                 answer->i_status = 400;
  646.                 answer->psz_status = strdup( "Internal server error" );
  647.                 answer->i_body = 0;
  648.                 answer->p_body = NULL;
  649.                 break;
  650.             }
  651.             fprintf( stderr, "HTTPD_MSG_SETUP: unicast ip=%s port=%dn",
  652.                      ip, i_port );
  653.             psz_session = httpd_MsgGet( query, "Session" );
  654.             if( !psz_session || !*psz_session )
  655.             {
  656.                 asprintf( &psz_session, "%d", rand() );
  657.                 p_rtsp = RtspClientNew( p_media, psz_session );
  658.             }
  659.             else
  660.             {
  661.                 p_rtsp = RtspClientGet( p_media, psz_session );
  662.                 if( !p_rtsp )
  663.                 {
  664.                     /* FIXME right error code */
  665.                     answer->i_status = 400;
  666.                     answer->psz_status = strdup( "Unknown session id" );
  667.                     answer->i_body = 0;
  668.                     answer->p_body = NULL;
  669.                     free( ip );
  670.                     break;
  671.                 }
  672.             }
  673.             p_rtsp_es = malloc( sizeof(rtsp_client_es_t) );
  674.             p_rtsp_es->i_port = i_port;
  675.             p_rtsp_es->psz_ip = strdup( ip );
  676.             p_rtsp_es->p_media_es = p_es;
  677.             TAB_APPEND( p_rtsp->i_es, p_rtsp->es, p_rtsp_es );
  678.             answer->i_status = 200;
  679.             answer->psz_status = strdup( "OK" );
  680.             answer->i_body = 0;
  681.             answer->p_body = NULL;
  682.             httpd_MsgAdd( answer, "Transport", "RTP/AVP/UDP;client_port=%d-%d",
  683.                           i_port, i_port + 1 );
  684.         }
  685.         else /* TODO  strstr( psz_transport, "interleaved" ) ) */
  686.         {
  687.             answer->i_status = 400;
  688.             answer->psz_status = strdup( "Bad Request" );
  689.             answer->i_body = 0;
  690.             answer->p_body = NULL;
  691.         }
  692.         break;
  693.         case HTTPD_MSG_TEARDOWN:
  694.         {
  695.             rtsp_client_t *p_rtsp;
  696.             answer->i_status = 200;
  697.             answer->psz_status = strdup( "OK" );
  698.             answer->i_body = 0;
  699.             answer->p_body = NULL;
  700.             psz_session = httpd_MsgGet( query, "Session" );
  701.             msg_Dbg( p_vod, "HTTPD_MSG_TEARDOWN for session: %s", psz_session);
  702.             p_rtsp = RtspClientGet( p_media, psz_session );
  703.             if( !p_rtsp ) break;
  704.             for( i = 0; i < p_rtsp->i_es; i++ )
  705.             {
  706.                 if( p_rtsp->es[i]->p_media_es == p_es )
  707.                 {
  708.                     if( p_rtsp->es[i]->psz_ip ) free( p_rtsp->es[i]->psz_ip );
  709.                     TAB_REMOVE( p_rtsp->i_es, p_rtsp->es, p_rtsp->es[i] );
  710.                     break;
  711.                 }
  712.             }
  713.             if( !p_rtsp->i_es )
  714.             {
  715.                 vod_MediaControl( p_vod, p_media, psz_session,
  716.                                   VOD_MEDIA_STOP );
  717.                 RtspClientDel( p_media, p_rtsp );
  718.             }
  719.             break;
  720.         }
  721.         case HTTPD_MSG_PLAY:
  722.         case HTTPD_MSG_PAUSE:
  723.             answer->i_status = 460;
  724.             answer->psz_status = strdup( "Only Aggregate Operation Allowed" );
  725.             answer->i_body = 0;
  726.             answer->p_body = NULL;
  727.             break;
  728.         default:
  729.             return VLC_EGENERIC;
  730.             break;
  731.     }
  732.     httpd_MsgAdd( answer, "Server", "VLC Server" );
  733.     httpd_MsgAdd( answer, "Content-Length", "%d", answer->i_body );
  734.     httpd_MsgAdd( answer, "Cseq", "%d",
  735.                   atoi( httpd_MsgGet( query, "Cseq" ) ) );
  736.     httpd_MsgAdd( answer, "Cache-Control", "%s", "no-cache" );
  737.     if( psz_session )
  738.     {
  739.         httpd_MsgAdd( answer, "Session", "%s"/*;timeout=5*/, psz_session );
  740.     }
  741.     return VLC_SUCCESS;
  742. }
  743. /*****************************************************************************
  744.  * SDPGenerate: TODO
  745.  * FIXME: need to be moved to a common place ?
  746.  *****************************************************************************/
  747. static char *SDPGenerate( vod_media_t *p_media, char *psz_destination )
  748. {
  749.     int i, i_size;
  750.     char *p, *psz_sdp;
  751.     /* Calculate size */
  752.     i_size = strlen( "v=0rn" ) +
  753.         strlen( "o=- * * IN IP4 127.0.0.1rn" ) + 10 + 10 +
  754.         strlen( "s=*rn" ) + strlen( p_media->psz_session_name ) +
  755.         strlen( "i=*rn" ) + strlen( p_media->psz_session_description ) +
  756.         strlen( "u=*rn" ) + strlen( p_media->psz_session_url ) +
  757.         strlen( "e=*rn" ) + strlen( p_media->psz_session_email ) +
  758.         strlen( "t=0 0rn" ) + /* FIXME */
  759.         strlen( "a=tool:"PACKAGE_STRING"rn" ) +
  760.         strlen( "c=IN IP4 */*rn" ) + 20 + 10 +
  761.         strlen( psz_destination ? psz_destination : "0.0.0.0" ) ;
  762.     for( i = 0; i < p_media->i_es; i++ )
  763.     {
  764.         media_es_t *p_es = p_media->es[i];
  765.         i_size += strlen( "m=**d*o * RTP/AVP *rn" ) + 10 + 10;
  766.         if( p_es->psz_rtpmap )
  767.         {
  768.             i_size += strlen( "a=rtpmap:* *rn" ) +
  769.                 strlen( p_es->psz_rtpmap ) + 10;
  770.         }
  771.         if( p_es->psz_fmtp )
  772.         {
  773.             i_size += strlen( "a=fmtp:* *rn" ) +
  774.                 strlen( p_es->psz_fmtp ) + 10;
  775.         }
  776.         i_size += strlen( "a=control:*/trackid=*rn" ) +
  777.             strlen( p_media->psz_rtsp_control ) + 10;
  778.     }
  779.     p = psz_sdp = malloc( i_size );
  780.     p += sprintf( p, "v=0rn" );
  781.     p += sprintf( p, "o=- "I64Fd" %d IN IP4 127.0.0.1rn",
  782.                   p_media->i_sdp_id, p_media->i_sdp_version );
  783.     if( *p_media->psz_session_name )
  784.         p += sprintf( p, "s=%srn", p_media->psz_session_name );
  785.     if( *p_media->psz_session_description )
  786.         p += sprintf( p, "i=%srn", p_media->psz_session_description );
  787.     if( *p_media->psz_session_url )
  788.         p += sprintf( p, "u=%srn", p_media->psz_session_url );
  789.     if( *p_media->psz_session_email )
  790.         p += sprintf( p, "e=%srn", p_media->psz_session_email );
  791.     p += sprintf( p, "t=0 0rn" ); /* FIXME */
  792.     p += sprintf( p, "a=tool:"PACKAGE_STRING"rn" );
  793.     p += sprintf( p, "c=IN IP4 %s/%drn", psz_destination ?
  794.                   psz_destination : "0.0.0.0", p_media->i_ttl );
  795.     for( i = 0; i < p_media->i_es; i++ )
  796.     {
  797.         media_es_t *p_es = p_media->es[i];
  798.         if( p_es->fmt.i_cat == AUDIO_ES )
  799.         {
  800.             p += sprintf( p, "m=audio %d RTP/AVP %drn",
  801.                           p_es->i_port, p_es->i_payload_type );
  802.         }
  803.         else if( p_es->fmt.i_cat == VIDEO_ES )
  804.         {
  805.             p += sprintf( p, "m=video %d RTP/AVP %drn",
  806.                           p_es->i_port, p_es->i_payload_type );
  807.         }
  808.         else
  809.         {
  810.             continue;
  811.         }
  812.         if( p_es->psz_rtpmap )
  813.         {
  814.             p += sprintf( p, "a=rtpmap:%d %srn", p_es->i_payload_type,
  815.                           p_es->psz_rtpmap );
  816.         }
  817.         if( p_es->psz_fmtp )
  818.         {
  819.             p += sprintf( p, "a=fmtp:%d %srn", p_es->i_payload_type,
  820.                           p_es->psz_fmtp );
  821.         }
  822.         p += sprintf( p, "a=control:%s/trackid=%drn",
  823.                       p_media->psz_rtsp_control, i );
  824.     }
  825.     fprintf( stderr, psz_sdp );
  826.     return psz_sdp;
  827. }