rtsp.c 53.8 KB
Newer Older
1 2 3
/*****************************************************************************
 * rtsp.c: rtsp VoD server module
 *****************************************************************************
4
 * Copyright (C) 2003-2006 the VideoLAN team
5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
 * $Id$
 *
 * Authors: Laurent Aimar <fenrir@via.ecp.fr>
 *          Gildas Bazin <gbazin@videolan.org>
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
Antoine Cellerier's avatar
Antoine Cellerier committed
22
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
23 24 25 26 27 28
 *****************************************************************************/

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

29 30 31 32
#ifdef HAVE_CONFIG_H
# include "config.h"
#endif

33
#include <vlc/vlc.h>
Clément Stenac's avatar
Clément Stenac committed
34 35
#include <vlc_input.h>
#include <vlc_sout.h>
36
#include <vlc_block.h>
37 38 39

#include "vlc_httpd.h"
#include "vlc_vod.h"
40
#include "vlc_url.h"
Clément Stenac's avatar
Clément Stenac committed
41 42
#include <vlc_network.h>
#include <vlc_charset.h>
Christophe Mutricy's avatar
Christophe Mutricy committed
43
#include <vlc_strings.h>
44

Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
45 46
#include <errno.h>

47 48 49 50 51 52
/*****************************************************************************
 * Module descriptor
 *****************************************************************************/
static int  Open ( vlc_object_t * );
static void Close( vlc_object_t * );

53
#define HOST_TEXT N_( "RTSP host address" )
54
#define HOST_LONGTEXT N_( \
55 56
    "This defines the address, port and path the RTSP VOD server will listen " \
    "on.\nSyntax is address:port/path. The default is to listen on all "\
Christophe Mutricy's avatar
Christophe Mutricy committed
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

60
#define THROTLE_TEXT N_( "Maximum number of connections" )
61 62
#define THROTLE_LONGTEXT N_( "This limits the maximum number of clients " \
    "that can connect to the RTSP VOD. 0 means no limit."  )
63

64 65
#define RAWMUX_TEXT N_( "MUX for RAW RTSP transport" )

66 67 68 69 70 71 72
#define SESSION_TIMEOUT_TEXT N_( "Sets the timeout option in the RTSP " \
    "session string" )
#define SESSION_TIMEOUT_LONGTEXT N_( "Defines what timeout option to add " \
    "to the RTSP session ID string. Setting it to a negative number removes " \
    "the timeout option entirely. This is needed by some IPTV STBs (such as " \
    "those made by HansunTech) which get confused by it. The default is 5." )

73
vlc_module_begin();
Clément Stenac's avatar
Clément Stenac committed
74
    set_shortname( _("RTSP VoD" ) );
75
    set_description( _("RTSP VoD server") );
76 77
    set_category( CAT_SOUT );
    set_subcategory( SUBCAT_SOUT_VOD );
78 79 80 81
    set_capability( "vod server", 1 );
    set_callbacks( Open, Close );
    add_shortcut( "rtsp" );
    add_string ( "rtsp-host", NULL, NULL, HOST_TEXT, HOST_LONGTEXT, VLC_TRUE );
82 83
    add_string( "rtsp-raw-mux", "ts", NULL, RAWMUX_TEXT,
                RAWMUX_TEXT, VLC_TRUE );
84 85
    add_integer( "rtsp-throttle-users", 0, NULL, THROTLE_TEXT,
                                           THROTLE_LONGTEXT, VLC_TRUE );
86 87
    add_integer( "rtsp-session-timeout", 5, NULL, SESSION_TIMEOUT_TEXT,
                 SESSION_TIMEOUT_LONGTEXT, VLC_TRUE );
88 89 90 91 92 93
vlc_module_end();

/*****************************************************************************
 * Exported prototypes
 *****************************************************************************/

94 95 96 97 98 99 100 101 102 103
typedef struct media_es_t media_es_t;

typedef struct
{
    media_es_t *p_media_es;
    char *psz_ip;
    int i_port;

} rtsp_client_es_t;

104 105 106 107 108 109
typedef struct
{
    char *psz_session;
    int64_t i_last; /* for timeout */

    vlc_bool_t b_playing; /* is it in "play" state */
110 111 112 113
    vlc_bool_t b_paused; /* is it in "pause" state */

    int i_es;
    rtsp_client_es_t **es;
114 115 116

} rtsp_client_t;

117
struct media_es_t
118 119 120 121 122
{
    /* VoD server */
    vod_t *p_vod;

    /* RTSP server */
123
    httpd_url_t *p_rtsp_url;
124 125 126

    vod_media_t *p_media;

127 128 129 130 131 132
    es_format_t fmt;
    int         i_port;
    uint8_t     i_payload_type;
    char        *psz_rtpmap;
    char        *psz_fmtp;

133
};
134 135 136

struct vod_media_t
{
137 138
    int id;

139 140 141 142 143
    /* VoD server */
    vod_t *p_vod;

    /* RTSP server */
    httpd_url_t  *p_rtsp_url;
144 145
    char         *psz_rtsp_control_v4;
    char         *psz_rtsp_control_v6;
146 147 148 149 150 151
    char         *psz_rtsp_path;

    int  i_port;
    int  i_port_audio;
    int  i_port_video;
    int  i_ttl;
152 153 154 155 156
    int  i_payload_type;

    int64_t i_sdp_id;
    int     i_sdp_version;

157 158
    vlc_bool_t b_multicast;

159
    vlc_mutex_t lock;
160 161 162 163

    /* ES list */
    int        i_es;
    media_es_t **es;
164
    char       *psz_mux;
165
    vlc_bool_t  b_raw;
166 167 168 169

    /* RTSP client */
    int           i_rtsp;
    rtsp_client_t **rtsp;
170 171 172 173 174 175

    /* Infos */
    char *psz_session_name;
    char *psz_session_description;
    char *psz_session_url;
    char *psz_session_email;
176
    mtime_t i_length;
177 178 179 180 181 182 183 184
};

struct vod_sys_t
{
    /* RTSP server */
    httpd_host_t *p_rtsp_host;
    char *psz_path;
    int i_port;
185
    int i_throttle_users;
186
    int i_connections;
187

188 189
    char *psz_raw_mux;

190 191
    int i_session_timeout;

192
    /* List of media */
193 194
    vlc_mutex_t lock_media;
    int i_media_id;
195 196
    int i_media;
    vod_media_t **media;
197 198 199

    /* */
    block_fifo_t *p_fifo_cmd;
200 201
};

202 203 204 205 206 207 208 209 210 211 212 213 214
/* rtsp delayed command (to avoid deadlock between vlm/httpd) */
typedef enum
{
    RTSP_CMD_TYPE_NONE,  /* Exit requested */

    RTSP_CMD_TYPE_PLAY,
    RTSP_CMD_TYPE_PAUSE,
    RTSP_CMD_TYPE_STOP,
    RTSP_CMD_TYPE_SEEK,
    RTSP_CMD_TYPE_REWIND,
    RTSP_CMD_TYPE_FORWARD,
} rtsp_cmd_type_t;

215
static vod_media_t *MediaNew( vod_t *, const char *, input_item_t * );
216 217 218 219
static void         MediaDel( vod_t *, vod_media_t * );
static int          MediaAddES( vod_t *, vod_media_t *, es_format_t * );
static void         MediaDelES( vod_t *, vod_media_t *, es_format_t * );

220 221 222 223
static void CommandThread( vlc_object_t *p_this );
static void CommandPush( vod_t *, rtsp_cmd_type_t, vod_media_t *, const char *psz_session,
                         double f_arg, const char *psz_arg );

224
static rtsp_client_t *RtspClientNew( vod_media_t *, char * );
225
static rtsp_client_t *RtspClientGet( vod_media_t *, const char * );
226 227 228
static void           RtspClientDel( vod_media_t *, rtsp_client_t * );

static int RtspCallback( httpd_callback_sys_t *, httpd_client_t *,
229
                         httpd_message_t *, const httpd_message_t * );
230
static int RtspCallbackES( httpd_callback_sys_t *, httpd_client_t *,
231
                           httpd_message_t *, const httpd_message_t * );
232

233
static char *SDPGenerate( const vod_media_t *, httpd_client_t *cl );
234

235 236 237 238 239 240 241 242 243 244 245 246 247
static void sprintf_hexa( char *s, uint8_t *p_data, int i_data )
{
    static const char hex[16] = "0123456789abcdef";
    int i;

    for( i = 0; i < i_data; i++ )
    {
        s[2*i+0] = hex[(p_data[i]>>4)&0xf];
        s[2*i+1] = hex[(p_data[i]   )&0xf];
    }
    s[2*i_data] = '\0';
}

248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267
/*****************************************************************************
 * Open: Starts the RTSP server module
 *****************************************************************************/
static int Open( vlc_object_t *p_this )
{
    vod_t *p_vod = (vod_t *)p_this;
    vod_sys_t *p_sys = 0;
    char *psz_url = 0;
    vlc_url_t url;

    psz_url = config_GetPsz( p_vod, "rtsp-host" );
    vlc_UrlParse( &url, psz_url, 0 );
    if( psz_url ) free( psz_url );

    if( url.i_port <= 0 ) url.i_port = 554;

    p_vod->p_sys = p_sys = malloc( sizeof( vod_sys_t ) );
    if( !p_sys ) goto error;
    p_sys->p_rtsp_host = 0;

268
    p_sys->i_session_timeout = var_CreateGetInteger( p_this, "rtsp-session-timeout" );
269

270
    p_sys->i_throttle_users = var_CreateGetInteger( p_this, "rtsp-throttle-users" );
271
    msg_Dbg( p_this, "allowing up to %d connections", p_sys->i_throttle_users );
272 273
    p_sys->i_connections = 0;

274
    p_sys->psz_raw_mux = var_CreateGetString( p_this, "rtsp-raw-mux" );
275

276 277 278 279
    p_sys->p_rtsp_host =
        httpd_HostNew( VLC_OBJECT(p_vod), url.psz_host, url.i_port );
    if( !p_sys->p_rtsp_host )
    {
280
        msg_Err( p_vod, "cannot create RTSP server (%s:%i)",
281 282 283 284 285 286 287 288
                 url.psz_host, url.i_port );
        goto error;
    }

    p_sys->psz_path = strdup( url.psz_path ? url.psz_path : "/" );
    p_sys->i_port = url.i_port;

    vlc_UrlClean( &url );
289 290 291 292 293

    vlc_mutex_init( p_vod, &p_sys->lock_media );

    TAB_INIT( p_sys->i_media, p_sys->media );
    p_sys->i_media_id = 0;
294 295 296 297 298 299

    p_vod->pf_media_new = MediaNew;
    p_vod->pf_media_del = MediaDel;
    p_vod->pf_media_add_es = MediaAddES;
    p_vod->pf_media_del_es = MediaDelES;

300 301 302 303 304 305 306 307 308 309
    p_sys->p_fifo_cmd = block_FifoNew( p_vod );
    if( vlc_thread_create( p_vod, "rtsp vod thread", CommandThread,
                           VLC_THREAD_PRIORITY_LOW, VLC_FALSE ) )
    {
        msg_Err( p_vod, "cannot spawn rtsp vod thread" );
        block_FifoRelease( p_sys->p_fifo_cmd );
        free( p_sys->psz_path );
        goto error;
    }

310 311
    return VLC_SUCCESS;

312
error:
313
    if( p_sys && p_sys->p_rtsp_host ) httpd_HostDelete( p_sys->p_rtsp_host );
314
    if( p_sys && p_sys->psz_raw_mux ) free( p_sys->psz_raw_mux );
315 316
    if( p_sys ) free( p_sys );
    vlc_UrlClean( &url );
317

318 319 320 321 322 323 324 325 326 327 328
    return VLC_EGENERIC;
}

/*****************************************************************************
 * Close:
 *****************************************************************************/
static void Close( vlc_object_t * p_this )
{
    vod_t *p_vod = (vod_t *)p_this;
    vod_sys_t *p_sys = p_vod->p_sys;

329
    /* Stop command thread */
330
    vlc_object_kill( p_vod );
331 332 333 334 335
    CommandPush( p_vod, RTSP_CMD_TYPE_NONE, NULL, NULL, 0.0, NULL );
    vlc_thread_join( p_vod );

    block_FifoRelease( p_sys->p_fifo_cmd );

336
    httpd_HostDelete( p_sys->p_rtsp_host );
337
    var_Destroy( p_this, "rtsp-session-timeout" );
338
    var_Destroy( p_this, "rtsp-throttle-users" );
339
    var_Destroy( p_this, "rtsp-raw-mux" );
340

341 342 343 344 345 346 347
    /* Check VLM is not buggy */
    if( p_sys->i_media > 0 )
        msg_Err( p_vod, "rtsp vod leaking %d medias", p_sys->i_media );
    TAB_CLEAN( p_sys->i_media, p_sys->media );

    vlc_mutex_destroy( &p_sys->lock_media );

Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
348
    free( p_sys->psz_path );
349
    free( p_sys->psz_raw_mux );
350 351 352 353 354 355
    free( p_sys );
}

/*****************************************************************************
 * Media handling
 *****************************************************************************/
356
static vod_media_t *MediaNew( vod_t *p_vod, const char *psz_name,
357
                              input_item_t *p_item )
358 359 360
{
    vod_sys_t *p_sys = p_vod->p_sys;
    vod_media_t *p_media = malloc( sizeof(vod_media_t) );
361
    int i;
362

Jean-Paul Saman's avatar
Jean-Paul Saman committed
363 364 365 366 367 368
    if( !p_media )
    {
        msg_Err( p_vod, "not enough memory" );
        return NULL;
    }

369
    memset( p_media, 0, sizeof(vod_media_t) );
370 371
    p_media->id = p_sys->i_media_id++;
    TAB_INIT( p_media->i_es, p_media->es );
372
    p_media->psz_mux = 0;
373
    TAB_INIT( p_media->i_rtsp, p_media->rtsp );
374
    p_media->b_raw = VLC_FALSE;
375

376 377 378
    if( asprintf( &p_media->psz_rtsp_path, "%s%s",
                  p_sys->psz_path, psz_name ) <0 )
        return NULL;
379
    p_media->p_rtsp_url =
380 381
        httpd_UrlNewUnique( p_sys->p_rtsp_host, p_media->psz_rtsp_path, NULL,
                            NULL, NULL );
382 383 384

    if( !p_media->p_rtsp_url )
    {
385
        msg_Err( p_vod, "cannot create RTSP url (%s)", p_media->psz_rtsp_path);
386 387
        free( p_media->psz_rtsp_path );
        free( p_media );
Jean-Paul Saman's avatar
Jean-Paul Saman committed
388
        return NULL;
389 390
    }

391
    msg_Dbg( p_vod, "created RTSP url: %s", p_media->psz_rtsp_path );
392

393
    if( asprintf( &p_media->psz_rtsp_control_v4,
394
               "a=control:rtsp://%%s:%d%s/trackID=%%d\r\n",
395 396 397
               p_sys->i_port, p_media->psz_rtsp_path ) < 0 )
        return NULL;
    if( asprintf( &p_media->psz_rtsp_control_v6,
398
               "a=control:rtsp://[%%s]:%d%s/trackID=%%d\r\n",
399 400
              p_sys->i_port, p_media->psz_rtsp_path ) < 0 )
        return NULL;
401

402 403
    httpd_UrlCatch( p_media->p_rtsp_url, HTTPD_MSG_SETUP,
                    RtspCallback, (void*)p_media );
404 405 406 407 408 409
    httpd_UrlCatch( p_media->p_rtsp_url, HTTPD_MSG_DESCRIBE,
                    RtspCallback, (void*)p_media );
    httpd_UrlCatch( p_media->p_rtsp_url, HTTPD_MSG_PLAY,
                    RtspCallback, (void*)p_media );
    httpd_UrlCatch( p_media->p_rtsp_url, HTTPD_MSG_PAUSE,
                    RtspCallback, (void*)p_media );
410 411
    httpd_UrlCatch( p_media->p_rtsp_url, HTTPD_MSG_GETPARAMETER,
                    RtspCallback, (void*)p_media );
412 413 414 415 416
    httpd_UrlCatch( p_media->p_rtsp_url, HTTPD_MSG_TEARDOWN,
                    RtspCallback, (void*)p_media );

    p_media->p_vod = p_vod;

417
    vlc_mutex_lock( &p_sys->lock_media );
418
    TAB_APPEND( p_sys->i_media, p_sys->media, p_media );
419
    vlc_mutex_unlock( &p_sys->lock_media );
420

421 422 423 424 425 426 427 428 429 430 431 432 433
    vlc_mutex_init( p_vod, &p_media->lock );
    p_media->psz_session_name = strdup("");
    p_media->psz_session_description = strdup("");
    p_media->psz_session_url = strdup("");
    p_media->psz_session_email = strdup("");

    p_media->i_port_audio = 1234;
    p_media->i_port_video = 1236;
    p_media->i_port       = 1238;
    p_media->i_payload_type = 96;

    p_media->i_sdp_id = mdate();
    p_media->i_sdp_version = 1;
434
    p_media->i_length = input_item_GetDuration( p_item );
435

436 437 438
    vlc_mutex_lock( &p_item->lock );
    msg_Dbg( p_vod, "media has %i declared ES", p_item->i_es );
    for( i = 0; i < p_item->i_es; i++ )
439 440 441
    {
        MediaAddES( p_vod, p_media, p_item->es[i] );
    }
442 443
    vlc_mutex_unlock( &p_item->lock );

444 445 446 447 448 449 450
    return p_media;
}

static void MediaDel( vod_t *p_vod, vod_media_t *p_media )
{
    vod_sys_t *p_sys = p_vod->p_sys;

451 452
    msg_Dbg( p_vod, "deleting media: %s", p_media->psz_rtsp_path );

453 454 455 456 457 458 459
    vlc_mutex_lock( &p_sys->lock_media );
    TAB_REMOVE( p_sys->i_media, p_sys->media, p_media );
    vlc_mutex_unlock( &p_sys->lock_media );

    while( p_media->i_rtsp > 0 )
        RtspClientDel( p_media, p_media->rtsp[0] );
    TAB_CLEAN( p_media->i_rtsp, p_media->rtsp );
460

461 462
    httpd_UrlDelete( p_media->p_rtsp_url );
    if( p_media->psz_rtsp_path ) free( p_media->psz_rtsp_path );
463 464
    if( p_media->psz_rtsp_control_v6 ) free( p_media->psz_rtsp_control_v6 );
    if( p_media->psz_rtsp_control_v4 ) free( p_media->psz_rtsp_control_v4 );
465

466 467 468
    while( p_media->i_es )
        MediaDelES( p_vod, p_media, &p_media->es[0]->fmt );
    TAB_CLEAN( p_media->i_es, p_media->es );
469 470

    vlc_mutex_destroy( &p_media->lock );
471 472

    if( p_media->psz_session_name ) free( p_media->psz_session_name );
473
    if( p_media->psz_session_description )
474
        free( p_media->psz_session_description );
475 476 477
    if( p_media->psz_session_url ) free( p_media->psz_session_url );
    if( p_media->psz_session_email ) free( p_media->psz_session_email );
    if( p_media->psz_mux ) free( p_media->psz_mux );
478 479 480 481 482 483
    free( p_media );
}

static int MediaAddES( vod_t *p_vod, vod_media_t *p_media, es_format_t *p_fmt )
{
    media_es_t *p_es = malloc( sizeof(media_es_t) );
484
    char *psz_urlc;
485

486
    if( !p_es ) return VLC_ENOMEM;
487
    memset( p_es, 0, sizeof(media_es_t) );
488 489

    if( p_media->psz_mux ) free( p_media->psz_mux );
490
    p_media->psz_mux = NULL;
491 492

    /* TODO: update SDP, etc... */
493 494 495
    if( asprintf( &psz_urlc, "%s/trackID=%d",
              p_media->psz_rtsp_path, p_media->i_es ) < 0 )
        return VLC_ENOMEM;
496
    msg_Dbg( p_vod, "  - ES %4.4s (%s)", (char *)&p_fmt->i_codec, psz_urlc );
497

498
    switch( p_fmt->i_codec )
499
    {
500 501 502 503 504
        case VLC_FOURCC( 's', '1', '6', 'b' ):
            if( p_fmt->audio.i_channels == 1 && p_fmt->audio.i_rate == 44100 )
            {
                p_es->i_payload_type = 11;
            }
505
            else if( p_fmt->audio.i_channels == 2 &&
506
                     p_fmt->audio.i_rate == 44100 )
507 508 509 510 511 512 513 514 515 516 517 518
            {
                p_es->i_payload_type = 10;
            }
            else
            {
                p_es->i_payload_type = p_media->i_payload_type++;
            }
            p_es->psz_rtpmap = malloc( strlen( "L16/*/*" ) + 20+1 );
            sprintf( p_es->psz_rtpmap, "L16/%d/%d", p_fmt->audio.i_rate,
                    p_fmt->audio.i_channels );
            break;
        case VLC_FOURCC( 'u', '8', ' ', ' ' ):
519
            p_es->i_payload_type = p_media->i_payload_type++;
520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539
            p_es->psz_rtpmap = malloc( strlen( "L8/*/*" ) + 20+1 );
            sprintf( p_es->psz_rtpmap, "L8/%d/%d", p_fmt->audio.i_rate,
                    p_fmt->audio.i_channels );
            break;
        case VLC_FOURCC( 'm', 'p', 'g', 'a' ):
            p_es->i_payload_type = 14;
            p_es->psz_rtpmap = strdup( "MPA/90000" );
            break;
        case VLC_FOURCC( 'm', 'p', 'g', 'v' ):
            p_es->i_payload_type = 32;
            p_es->psz_rtpmap = strdup( "MPV/90000" );
            break;
        case VLC_FOURCC( 'a', '5', '2', ' ' ):
            p_es->i_payload_type = p_media->i_payload_type++;
            p_es->psz_rtpmap = strdup( "ac3/90000" );
            break;
        case VLC_FOURCC( 'H', '2', '6', '3' ):
            p_es->i_payload_type = p_media->i_payload_type++;
            p_es->psz_rtpmap = strdup( "H263-1998/90000" );
            break;
540 541 542
        case VLC_FOURCC( 'h', '2', '6', '4' ):
            p_es->i_payload_type = p_media->i_payload_type++;
            p_es->psz_rtpmap = strdup( "H264/90000" );
543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584
            p_es->psz_fmtp = NULL;
            /* FIXME AAAAAAAAAAAARRRRRRRRGGGG copied from stream_out/rtp.c */
            if( p_fmt->i_extra > 0 )
            {
                uint8_t *p_buffer = p_fmt->p_extra;
                int     i_buffer = p_fmt->i_extra;
                char    *p_64_sps = NULL;
                char    *p_64_pps = NULL;
                char    hexa[6+1];

                while( i_buffer > 4 &&
                       p_buffer[0] == 0 && p_buffer[1] == 0 &&
                       p_buffer[2] == 0 && p_buffer[3] == 1 )
                {
                    const int i_nal_type = p_buffer[4]&0x1f;
                    int i_offset;
                    int i_size      = 0;

                    i_size = i_buffer;
                    for( i_offset = 4; i_offset+3 < i_buffer ; i_offset++)
                    {
                        if( p_buffer[i_offset] == 0 && p_buffer[i_offset+1] == 0 && p_buffer[i_offset+2] == 0 && p_buffer[i_offset+3] == 1 )
                        {
                            /* we found another startcode */
                            i_size = i_offset;
                            break;
                        }
                    }
                    if( i_nal_type == 7 )
                    {
                        p_64_sps = vlc_b64_encode_binary( &p_buffer[4], i_size - 4 );
                        sprintf_hexa( hexa, &p_buffer[5], 3 );
                    }
                    else if( i_nal_type == 8 )
                    {
                        p_64_pps = vlc_b64_encode_binary( &p_buffer[4], i_size - 4 );
                    }
                    i_buffer -= i_size;
                    p_buffer += i_size;
                }
                /* */
                if( p_64_sps && p_64_pps )
585
                    if( asprintf( &p_es->psz_fmtp,
586 587 588 589
                                  "packetization-mode=1;profile-level-id=%s;"
                                  "sprop-parameter-sets=%s,%s;", hexa, p_64_sps,
                                  p_64_pps ) < 0 )
                        return VLC_ENOMEM;
590 591 592 593 594 595 596
                if( p_64_sps )
                    free( p_64_sps );
                if( p_64_pps )
                    free( p_64_pps );
            }
            if( !p_es->psz_fmtp )
                p_es->psz_fmtp = strdup( "packetization-mode=1" );
597
            break;
598 599 600 601 602 603 604 605 606 607 608 609 610 611 612 613 614 615 616 617 618 619 620 621 622 623 624 625 626 627
        case VLC_FOURCC( 'm', 'p', '4', 'v' ):
            p_es->i_payload_type = p_media->i_payload_type++;
            p_es->psz_rtpmap = strdup( "MP4V-ES/90000" );
            if( p_fmt->i_extra > 0 )
            {
                char *p_hexa = malloc( 2 * p_fmt->i_extra + 1 );
                p_es->psz_fmtp = malloc( 100 + 2 * p_fmt->i_extra );
                sprintf_hexa( p_hexa, p_fmt->p_extra, p_fmt->i_extra );
                sprintf( p_es->psz_fmtp,
                        "profile-level-id=3; config=%s;", p_hexa );
                free( p_hexa );
            }
            break;
        case VLC_FOURCC( 'm', 'p', '4', 'a' ):
            p_es->i_payload_type = p_media->i_payload_type++;
            p_es->psz_rtpmap = malloc( strlen( "mpeg4-generic/" ) + 12 );
            sprintf( p_es->psz_rtpmap, "mpeg4-generic/%d", p_fmt->audio.i_rate );
            if( p_fmt->i_extra > 0 )
            {
                char *p_hexa = malloc( 2 * p_fmt->i_extra + 1 );
                p_es->psz_fmtp = malloc( 200 + 2 * p_fmt->i_extra );
                sprintf_hexa( p_hexa, p_fmt->p_extra, p_fmt->i_extra );
                sprintf( p_es->psz_fmtp,
                        "streamtype=5; profile-level-id=15; mode=AAC-hbr; "
                        "config=%s; SizeLength=13;IndexLength=3; "
                        "IndexDeltaLength=3; Profile=1;", p_hexa );
                free( p_hexa );
            }
            break;
        case VLC_FOURCC( 'm', 'p', '2', 't' ):
628
            p_media->psz_mux = strdup("ts");
629 630 631 632
            p_es->i_payload_type = 33;
            p_es->psz_rtpmap = strdup( "MP2T/90000" );
            break;
        case VLC_FOURCC( 'm', 'p', '2', 'p' ):
633
            p_media->psz_mux = strdup("ps");
634 635 636 637 638 639 640 641 642 643 644 645 646 647 648
            p_es->i_payload_type = p_media->i_payload_type++;
            p_es->psz_rtpmap = strdup( "MP2P/90000" );
            break;
        case VLC_FOURCC( 's', 'a', 'm', 'r' ):
            p_es->i_payload_type = p_media->i_payload_type++;
            p_es->psz_rtpmap = strdup( p_fmt->audio.i_channels == 2 ?
                                    "AMR/8000/2" : "AMR/8000" );
            p_es->psz_fmtp = strdup( "octet-align=1" );
            break;
        case VLC_FOURCC( 's', 'a', 'w', 'b' ):
            p_es->i_payload_type = p_media->i_payload_type++;
            p_es->psz_rtpmap = strdup( p_fmt->audio.i_channels == 2 ?
                                    "AMR-WB/16000/2" : "AMR-WB/16000" );
            p_es->psz_fmtp = strdup( "octet-align=1" );
            break;
649

650 651 652 653 654
        default:
            msg_Err( p_vod, "cannot add this stream (unsupported "
                    "codec: %4.4s)", (char*)&p_fmt->i_codec );
            free( p_es );
            return VLC_EGENERIC;
655 656 657
    }

    p_es->p_rtsp_url =
658 659
        httpd_UrlNewUnique( p_vod->p_sys->p_rtsp_host, psz_urlc, NULL, NULL,
                            NULL );
660 661 662

    if( !p_es->p_rtsp_url )
    {
663
        msg_Err( p_vod, "cannot create RTSP url (%s)", psz_urlc );
664 665 666
        free( psz_urlc );
        free( p_es );
        return VLC_EGENERIC;
667
    }
668 669 670 671
    free( psz_urlc );

    httpd_UrlCatch( p_es->p_rtsp_url, HTTPD_MSG_SETUP,
                    RtspCallbackES, (void*)p_es );
672 673 674 675 676 677
    httpd_UrlCatch( p_es->p_rtsp_url, HTTPD_MSG_TEARDOWN,
                    RtspCallbackES, (void*)p_es );
    httpd_UrlCatch( p_es->p_rtsp_url, HTTPD_MSG_PLAY,
                    RtspCallbackES, (void*)p_es );
    httpd_UrlCatch( p_es->p_rtsp_url, HTTPD_MSG_PAUSE,
                    RtspCallbackES, (void*)p_es );
678

679
    es_format_Copy( &p_es->fmt, p_fmt );
680 681 682
    p_es->p_vod = p_vod;
    p_es->p_media = p_media;

683
#if 0
684 685 686 687 688 689 690 691 692 693 694 695 696 697 698 699 700 701 702 703 704 705
    /* Choose the port */
    if( p_fmt->i_cat == AUDIO_ES && p_media->i_port_audio > 0 )
    {
        p_es->i_port = p_media->i_port_audio;
        p_media->i_port_audio = 0;
    }
    else if( p_fmt->i_cat == VIDEO_ES && p_media->i_port_video > 0 )
    {
        p_es->i_port = p_media->i_port_video;
        p_media->i_port_video = 0;
    }
    while( !p_es->i_port )
    {
        if( p_media->i_port != p_media->i_port_audio &&
            p_media->i_port != p_media->i_port_video )
        {
            p_es->i_port = p_media->i_port;
            p_media->i_port += 2;
            break;
        }
        p_media->i_port += 2;
    }
706 707 708 709
#else

    p_es->i_port = 0;
#endif
710 711 712 713 714 715 716

    vlc_mutex_lock( &p_media->lock );
    TAB_APPEND( p_media->i_es, p_media->es, p_es );
    vlc_mutex_unlock( &p_media->lock );

    p_media->i_sdp_version++;

717 718 719 720 721
    return VLC_SUCCESS;
}

static void MediaDelES( vod_t *p_vod, vod_media_t *p_media, es_format_t *p_fmt)
{
722
    media_es_t *p_es = NULL;
723 724 725 726 727 728 729 730 731 732 733 734 735
    int i;

    /* Find the ES */
    for( i = 0; i < p_media->i_es; i++ )
    {
        if( p_media->es[i]->fmt.i_cat == p_fmt->i_cat &&
            p_media->es[i]->fmt.i_codec == p_fmt->i_codec &&
            p_media->es[i]->fmt.i_id == p_fmt->i_id )
        {
            p_es = p_media->es[i];
        }
    }
    if( !p_es ) return;
736

737 738 739
    msg_Dbg( p_vod, "  - Removing ES %4.4s", (char *)&p_fmt->i_codec );

    vlc_mutex_lock( &p_media->lock );
740
    TAB_REMOVE( p_media->i_es, p_media->es, p_es );
741
    vlc_mutex_unlock( &p_media->lock );
742

743 744 745
    if( p_es->psz_rtpmap ) free( p_es->psz_rtpmap );
    if( p_es->psz_fmtp ) free( p_es->psz_fmtp );
    p_media->i_sdp_version++;
746 747

    if( p_es->p_rtsp_url ) httpd_UrlDelete( p_es->p_rtsp_url );
748
    es_format_Clean( &p_es->fmt );
749
    free( p_es );
750 751
}

752 753 754 755 756 757 758 759 760 761 762 763 764 765 766 767 768 769 770 771 772 773 774 775 776 777 778 779 780 781 782 783 784 785 786 787 788 789 790 791 792 793 794 795 796 797 798 799 800 801 802 803 804 805 806 807 808 809 810 811 812 813 814 815 816 817 818 819 820 821 822 823 824 825 826 827 828 829 830 831 832 833 834 835 836 837 838 839 840 841 842 843 844 845 846 847 848 849 850 851 852 853 854 855 856 857 858 859
/* */
typedef struct
{
    int i_type;
    int i_media_id;
    //vod_media_t *p_media;
    char *psz_session;
    char *psz_arg;
    double f_arg;
} rtsp_cmd_t;

static void CommandPush( vod_t *p_vod, rtsp_cmd_type_t i_type, vod_media_t *p_media, const char *psz_session,
                         double f_arg, const char *psz_arg )
{
    rtsp_cmd_t cmd;
    block_t *p_cmd;

    memset( &cmd, 0, sizeof(cmd) );
    cmd.i_type = i_type;
    if( p_media )
        cmd.i_media_id = p_media->id;
    if( psz_session )
        cmd.psz_session = strdup(psz_session);
    cmd.f_arg = f_arg;
    if( psz_arg )
        cmd.psz_arg = strdup(psz_arg);

    p_cmd = block_New( p_vod, sizeof(rtsp_cmd_t) );
    memcpy( p_cmd->p_buffer, &cmd, sizeof(cmd) );

    block_FifoPut( p_vod->p_sys->p_fifo_cmd, p_cmd );
}

static void CommandThread( vlc_object_t *p_this )
{
    vod_t *p_vod = (vod_t*)p_this;
    vod_sys_t *p_sys = p_vod->p_sys;

    while( !p_vod->b_die )
    {
        block_t *p_block_cmd = block_FifoGet( p_sys->p_fifo_cmd );
        rtsp_cmd_t cmd;
        vod_media_t *p_media = NULL;
        int i;

        if( !p_block_cmd )
            break;

        memcpy( &cmd, p_block_cmd->p_buffer, sizeof(cmd) );
        block_Release( p_block_cmd );

        if( cmd.i_type == RTSP_CMD_TYPE_NONE )
            break;

        /* */
        vlc_mutex_lock( &p_sys->lock_media );
        for( i = 0; i < p_sys->i_media; i++ )
        {
            if( p_sys->media[i]->id == cmd.i_media_id )
                break;
        }
        if( i >= p_sys->i_media )
            goto next;
        p_media = p_sys->media[i];

        switch( cmd.i_type )
        {
        case RTSP_CMD_TYPE_PLAY:
            vod_MediaControl( p_vod, p_media, cmd.psz_session,
                              VOD_MEDIA_PLAY, cmd.psz_arg );
            break;
        case RTSP_CMD_TYPE_PAUSE:
            vod_MediaControl( p_vod, p_media, cmd.psz_session,
                              VOD_MEDIA_PAUSE );
            break;

        case RTSP_CMD_TYPE_STOP:
            vod_MediaControl( p_vod, p_media, cmd.psz_session, VOD_MEDIA_STOP );
            break;

        case RTSP_CMD_TYPE_SEEK:
            vod_MediaControl( p_vod, p_media, cmd.psz_session,
                              VOD_MEDIA_SEEK, cmd.f_arg );
            break;

        case RTSP_CMD_TYPE_REWIND:
            vod_MediaControl( p_vod, p_media, cmd.psz_session,
                              VOD_MEDIA_REWIND, cmd.f_arg );
            break;

        case RTSP_CMD_TYPE_FORWARD:
            vod_MediaControl( p_vod, p_media, cmd.psz_session,
                              VOD_MEDIA_FORWARD, cmd.f_arg );
            break;

        default:
            break;
        }

    next:
        vlc_mutex_unlock( &p_sys->lock_media );
        if( cmd.psz_session )
            free( cmd.psz_session );
        if( cmd.psz_arg )
            free( cmd.psz_arg );
    }
}

860 861 862 863 864
/****************************************************************************
 * RTSP server implementation
 ****************************************************************************/
static rtsp_client_t *RtspClientNew( vod_media_t *p_media, char *psz_session )
{
865
    rtsp_client_t *p_rtsp = malloc( sizeof(rtsp_client_t) );
866 867

    if( !p_rtsp ) return NULL;
868 869
    memset( p_rtsp, 0, sizeof(rtsp_client_t) );
    p_rtsp->es = 0;
870

871 872
    p_rtsp->psz_session = psz_session;
    TAB_APPEND( p_media->i_rtsp, p_media->rtsp, p_rtsp );
873

874 875
    p_media->p_vod->p_sys->i_connections++;
    msg_Dbg( p_media->p_vod, "new session: %s, connections: %d",
876
             psz_session, p_media->p_vod->p_sys->i_throttle_users );
877

878
    return p_rtsp;
879 880
}

881
static rtsp_client_t *RtspClientGet( vod_media_t *p_media, const char *psz_session )
882 883 884
{
    int i;

885
    for( i = 0; psz_session && i < p_media->i_rtsp; i++ )
886 887 888 889 890 891 892 893
    {
        if( !strcmp( p_media->rtsp[i]->psz_session, psz_session ) )
            return p_media->rtsp[i];
    }

    return NULL;
}

894
static void RtspClientDel( vod_media_t *p_media, rtsp_client_t *p_rtsp )
895
{
896 897
    p_media->p_vod->p_sys->i_connections--;
    msg_Dbg( p_media->p_vod, "closing session: %s, connections: %d",
898
             p_rtsp->psz_session, p_media->p_vod->p_sys->i_throttle_users );
899

900 901
    while( p_rtsp->i_es-- )
    {
902 903 904 905
        if( p_rtsp->es[p_rtsp->i_es]->psz_ip )
            free( p_rtsp->es[p_rtsp->i_es]->psz_ip );
        free( p_rtsp->es[p_rtsp->i_es] );
        if( !p_rtsp->i_es ) free( p_rtsp->es );
906 907
    }

908
    TAB_REMOVE( p_media->i_rtsp, p_media->rtsp, p_rtsp );
909

910 911
    free( p_rtsp->psz_session );
    free( p_rtsp );
912 913 914
}

static int RtspCallback( httpd_callback_sys_t *p_args, httpd_client_t *cl,
915
                         httpd_message_t *answer, const httpd_message_t *query )
916 917 918
{
    vod_media_t *p_media = (vod_media_t*)p_args;
    vod_t *p_vod = p_media->p_vod;
919 920 921 922
    const char *psz_transport = NULL;
    const char *psz_playnow = NULL; /* support option: x-playNow */
    const char *psz_session = NULL;
    const char *psz_cseq = NULL;
923
    rtsp_client_t *p_rtsp;
924
    int i_port = 0;
925
    int i_cseq = 0;
926 927 928

    if( answer == NULL || query == NULL ) return VLC_SUCCESS;

929
    msg_Dbg( p_vod, "RtspCallback query: type=%d", query->i_type );
930 931 932 933

    answer->i_proto   = HTTPD_PROTO_RTSP;
    answer->i_version = query->i_version;
    answer->i_type    = HTTPD_MSG_ANSWER;
934
    answer->i_body    = 0;
935
    answer->p_body    = NULL;
936 937 938

    switch( query->i_type )
    {
939 940
        case HTTPD_MSG_SETUP:
        {
941
            psz_playnow = httpd_MsgGet( query, "x-playNow" );
942
            psz_transport = httpd_MsgGet( query, "Transport" );
943 944 945 946 947
            if( psz_transport == NULL )
            {
                answer->i_status = 400;
                break;
            }
948 949 950 951 952
            msg_Dbg( p_vod, "HTTPD_MSG_SETUP: transport=%s", psz_transport );

            if( strstr( psz_transport, "unicast" ) &&
                strstr( psz_transport, "client_port=" ) )
            {
953
                rtsp_client_t *p_rtsp = NULL;
954
                char ip[NI_MAXNUMERICHOST];
955
                i_port = atoi( strstr( psz_transport, "client_port=" ) +
956 957
                                strlen("client_port=") );

958 959 960
                if( strstr( psz_transport, "MP2T/H2221/UDP" ) ||
                    strstr( psz_transport, "RAW/RAW/UDP" ) )
                {
961 962 963
                    if( p_media->psz_mux ) free( p_media->psz_mux );
                    p_media->psz_mux = NULL;
                    p_media->psz_mux = strdup( p_vod->p_sys->psz_raw_mux );
964
                    p_media->b_raw = VLC_TRUE;
965
                }
966

967 968 969 970 971 972 973 974 975
                if( httpd_ClientIP( cl, ip ) == NULL )
                {
                    answer->i_status = 500;
                    answer->i_body = 0;
                    answer->p_body = NULL;
                    break;
                }

                msg_Dbg( p_vod, "HTTPD_MSG_SETUP: unicast ip=%s port=%d",
976
                         ip, i_port );
977 978 979 980

                psz_session = httpd_MsgGet( query, "Session" );
                if( !psz_session || !*psz_session )
                {
981
                    char *psz_new;
982 983
                    if( ( p_vod->p_sys->i_throttle_users > 0 ) &&
                        ( p_vod->p_sys->i_connections >= p_vod->p_sys->i_throttle_users ) )
984
                    {
985
                        answer->i_status = 503;
986 987 988 989
                        answer->i_body = 0;
                        answer->p_body = NULL;
                        break;
                    }
990 991
                    if( asprintf( &psz_new, "%d", rand() ) < 0 )
                        return VLC_ENOMEM;
992 993 994
                    psz_session = psz_new;

                    p_rtsp = RtspClientNew( p_media, psz_new );
995 996 997 998 999 1000 1001
                    if( !p_rtsp )
                    {
                        answer->i_status = 454;
                        answer->i_body = 0;
                        answer->p_body = NULL;
                        break;
                    }
1002 1003 1004 1005 1006 1007 1008 1009 1010 1011 1012 1013 1014 1015 1016 1017 1018
                }
                else
                {
                    p_rtsp = RtspClientGet( p_media, psz_session );
                    if( !p_rtsp )
                    {
                        answer->i_status = 454;
                        answer->i_body = 0;
                        answer->p_body = NULL;
                        break;
                    }
                }

                answer->i_status = 200;
                answer->i_body = 0;
                answer->p_body = NULL;

1019 1020 1021 1022
                if( p_media->b_raw )
                {
                    if( strstr( psz_transport, "MP2T/H2221/UDP" ) )
                    {
1023 1024
                        httpd_MsgAdd( answer, "Transport",
                                      "MP2T/H2221/UDP;client_port=%d-%d",
1025 1026 1027 1028
                                      i_port, i_port + 1 );
                    }
                    else if( strstr( psz_transport, "RAW/RAW/UDP" ) )
                    {
1029 1030
                        httpd_MsgAdd( answer, "Transport",
                                      "RAW/RAW/UDP;client_port=%d-%d",
1031 1032 1033 1034
                                      i_port, i_port + 1 );
                    }
                }
                else
1035 1036
                    httpd_MsgAdd( answer, "Transport",
                                  "RTP/AVP/UDP;client_port=%d-%d",
1037
                                  i_port, i_port + 1 );
1038 1039 1040 1041 1042 1043 1044
            }
            else /* TODO  strstr( psz_transport, "interleaved" ) ) */
            {
                answer->i_status = 461;
                answer->i_body = 0;
                answer->p_body = NULL;
            }
1045

1046 1047
            /* Intentional fall-through on x-playNow option in RTSP request */
            if( !psz_playnow )
1048
                break;
1049 1050 1051 1052
        }

        case HTTPD_MSG_PLAY:
        {
1053
            char *psz_output, ip[NI_MAXNUMERICHOST];
1054
            int i, i_port_audio = 0, i_port_video = 0;
1055 1056

            /* for now only multicast so easy */
1057 1058 1059 1060 1061 1062
            if( !psz_playnow )
            {
                answer->i_status = 200;
                answer->i_body = 0;
                answer->p_body = NULL;
            }
1063

1064 1065
            if( !psz_session )
                psz_session = httpd_MsgGet( query, "Session" );
1066 1067
            msg_Dbg( p_vod, "HTTPD_MSG_PLAY for session: %s", psz_session );

1068
            p_rtsp = RtspClientGet( p_media, psz_session );
1069 1070 1071 1072 1073 1074 1075
            if( !p_rtsp )
            {
                answer->i_status = 500;
                answer->i_body = 0;
                answer->p_body = NULL;
                break;
            }
1076

1077 1078
            if( p_rtsp->b_playing )
            {
1079 1080
                const char *psz_position = httpd_MsgGet( query, "Range" );
                const char *psz_scale = httpd_MsgGet( query, "Scale" );
1081
                if( psz_position )
1082
                    psz_position = strstr( psz_position, "npt=" );
1083
                if( psz_position && !psz_scale )
1084 1085 1086 1087 1088 1089 1090 1091 1092 1093 1094
                {
                    double f_pos;
                    char *end;

                    msg_Dbg( p_vod, "seeking request: %s", psz_position );
                    psz_position += 4;
                    /* FIXME: npt= is not necessarily formatted as a float */
                    f_pos = us_strtod( psz_position, &end );
                    if( end > psz_position )
                    {
                        f_pos /= ((double)(p_media->i_length))/1000 /1000 / 100;
1095 1096
                        CommandPush( p_vod, RTSP_CMD_TYPE_SEEK, p_media,
                                     psz_session, f_pos, NULL );
1097 1098 1099
                    }
                    break;
                }
1100 1101 1102 1103 1104 1105 1106 1107 1108 1109 1110 1111
                if( psz_scale )
                {
                    double f_scale = 0.0;
                    char *end;

                    f_scale = us_strtod( psz_scale, &end );
                    if( end > psz_scale )
                    {
                        f_scale = (f_scale * 30.0);
                        if( psz_scale[0] == '-' ) /* rewind */
                        {
                            msg_Dbg( p_vod, "rewind request: %s", psz_scale );
1112
                            CommandPush( p_vod, RTSP_CMD_TYPE_REWIND, p_media,
1113
                                         psz_session, f_scale, NULL );
1114 1115 1116
                        }
                        else if(psz_scale[0] != '1' ) /* fast-forward */
                        {
1117
                            msg_Dbg( p_vod, "fastforward request: %s",
1118 1119 1120
                                     psz_scale );
                            CommandPush( p_vod, RTSP_CMD_TYPE_FORWARD, p_media,
                                         psz_session, f_scale, NULL );
1121 1122 1123 1124 1125
                        }

                        if( p_rtsp->b_paused == VLC_TRUE )
                        {
                            p_rtsp->b_paused = VLC_FALSE;
1126 1127
                            CommandPush( p_vod, RTSP_CMD_TYPE_PAUSE, p_media,
                                         psz_session, 0, NULL );
1128 1129 1130 1131
                        }
                    }
                    break;
                }
1132 1133
            }

1134 1135
            if( p_rtsp->b_playing && p_rtsp->b_paused )
            {
1136 1137
                CommandPush( p_vod, RTSP_CMD_TYPE_PAUSE, p_media,
                             psz_session, 0, NULL );
1138 1139 1140 1141 1142
                p_rtsp->b_paused = VLC_FALSE;
                break;
            }
            else if( p_rtsp->b_playing ) break;

1143
            if( httpd_ClientIP( cl, ip ) == NULL ) break;
1144

1145
            p_rtsp->b_playing = VLC_TRUE;
1146 1147

            /* FIXME for != 1 video and 1 audio */
1148
            for( i = 0; i < p_rtsp->i_es; i++ )
1149
            {
1150 1151 1152 1153
                if( p_rtsp->es[i]->p_media_es->fmt.i_cat == AUDIO_ES )
                    i_port_audio = p_rtsp->es[i]->i_port;
                if( p_rtsp->es[i]->p_media_es->fmt.i_cat == VIDEO_ES )
                    i_port_video = p_rtsp->es[i]->i_port;
1154
            }
1155

1156 1157
            if( p_media->psz_mux )
            {
1158 1159
                if( p_media->b_raw )
                {
1160
                    if( asprintf( &psz_output,
1161 1162 1163
                              "std{access=udp,dst=%s:%i,mux=%s}",
                              ip, i_port, p_media->psz_mux ) < 0 )
                        return VLC_ENOMEM;
1164 1165 1166
                }
                else
                {
1167
                    if( asprintf( &psz_output,
1168 1169 1170
                              "rtp{dst=%s,port=%i,mux=%s}",
                              ip, i_port_video, p_media->psz_mux ) < 0 )
                        return VLC_ENOMEM;
1171
                }
1172 1173 1174
            }
            else
            {
1175
                if( asprintf( &psz_output,
1176 1177 1178
                              "rtp{dst=%s,port-video=%i,port-audio=%i}",
                              ip, i_port_video, i_port_audio ) < 0 )
                    return VLC_ENOMEM;
1179 1180
            }

1181 1182
            CommandPush( p_vod, RTSP_CMD_TYPE_PLAY, p_media, psz_session,
                         0, psz_output );
1183
            free( psz_output );
1184 1185 1186
            break;
        }

1187 1188 1189 1190 1191 1192 1193 1194
        case HTTPD_MSG_DESCRIBE:
        {
            char *psz_sdp =
                SDPGenerate( p_media, cl );

            if( psz_sdp != NULL )
            {
                answer->i_status = 200;
1195 1196
                httpd_MsgAdd( answer, "Content-type",  "%s",
                              "application/sdp" );
1197 1198 1199 1200 1201 1202 1203 1204 1205 1206 1207 1208 1209

                answer->p_body = (uint8_t *)psz_sdp;
                answer->i_body = strlen( psz_sdp );
            }
            else
            {
                answer->i_status = 500;
                answer->p_body = NULL;
                answer->i_body = 0;
            }
            break;
        }

1210 1211 1212
        case HTTPD_MSG_PAUSE:
            psz_session = httpd_MsgGet( query, "Session" );
            msg_Dbg( p_vod, "HTTPD_MSG_PAUSE for session: %s", psz_session );
1213

1214 1215 1216
            p_rtsp = RtspClientGet( p_media, psz_session );
            if( !p_rtsp ) break;

1217 1218
            CommandPush( p_vod, RTSP_CMD_TYPE_PAUSE, p_media, psz_session,
                         0, NULL );
1219
            p_rtsp->b_paused = VLC_TRUE;
1220 1221 1222 1223 1224

            answer->i_status = 200;
            answer->i_body = 0;
            answer->p_body = NULL;
            break;
1225 1226 1227 1228 1229 1230 1231 1232 1233 1234

        case HTTPD_MSG_TEARDOWN:
            /* for now only multicast so easy again */
            answer->i_status = 200;
            answer->i_body = 0;
            answer->p_body = NULL;

            psz_session = httpd_MsgGet( query, "Session" );
            msg_Dbg( p_vod, "HTTPD_MSG_TEARDOWN for session: %s", psz_session);

1235 1236
            p_rtsp = RtspClientGet( p_media, psz_session );
            if( !p_rtsp ) break;
1237

1238 1239
            CommandPush( p_vod, RTSP_CMD_TYPE_STOP, p_media, psz_session,
                         0, NULL );
1240
            RtspClientDel( p_media, p_rtsp );
1241 1242
            break;

1243 1244 1245 1246 1247 1248
        case HTTPD_MSG_GETPARAMETER:
            answer->i_status = 200;
            answer->i_body = 0;
            answer->p_body = NULL;
            break;

1249 1250 1251 1252 1253 1254
        default:
            return VLC_EGENERIC;
    }

    httpd_MsgAdd( answer, "Server", "VLC Server" );
    httpd_MsgAdd( answer, "Content-Length", "%d", answer->i_body );
1255 1256
    psz_cseq = httpd_MsgGet( query, "Cseq" );
    psz_cseq ? i_cseq = atoi( psz_cseq ) : 0;
1257
    httpd_MsgAdd( answer, "CSeq", "%d", i_cseq );
1258 1259 1260 1261
    httpd_MsgAdd( answer, "Cache-Control", "%s", "no-cache" );

    if( psz_session )
    {
1262 1263 1264 1265 1266
         if( p_media->p_vod->p_sys->i_session_timeout >= 0 )
             httpd_MsgAdd( answer, "Session", "%s;timeout=%i", psz_session,
               p_media->p_vod->p_sys->i_session_timeout );
         else
              httpd_MsgAdd( answer, "Session", "%s", psz_session );
1267
    }
1268 1269 1270 1271

    return VLC_SUCCESS;
}

1272
static int RtspCallbackES( httpd_callback_sys_t *p_args, httpd_client_t *cl,
1273 1274
                           httpd_message_t *answer,
                           const httpd_message_t *query )
1275
{
1276 1277
    media_es_t *p_es = (media_es_t*)p_args;
    vod_media_t *p_media = p_es->p_media;
1278
    vod_t *p_vod = p_media->p_vod;