rtsp.c 53.1 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
    set_capability( "vod server", 1 );
    set_callbacks( Open, Close );
    add_shortcut( "rtsp" );
81
    add_string ( "rtsp-host", NULL, NULL, HOST_TEXT, HOST_LONGTEXT, true );
82
    add_string( "rtsp-raw-mux", "ts", NULL, RAWMUX_TEXT,
83
                RAWMUX_TEXT, true );
84
    add_integer( "rtsp-throttle-users", 0, NULL, THROTLE_TEXT,
85
                                           THROTLE_LONGTEXT, true );
86
    add_integer( "rtsp-session-timeout", 5, NULL, SESSION_TIMEOUT_TEXT,
87
                 SESSION_TIMEOUT_LONGTEXT, 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
typedef struct
{
    char *psz_session;
    int64_t i_last; /* for timeout */

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

    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
    bool b_multicast;
158

159
    vlc_mutex_t lock;
160 161 162 163

    /* ES list */
    int        i_es;
    media_es_t **es;
164
    char       *psz_mux;
165
    bool  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
/*****************************************************************************
 * 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 );
260
    free( psz_url );
261 262 263 264 265 266 267

    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
    vlc_mutex_init( &p_sys->lock_media );
291 292 293

    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
    p_sys->p_fifo_cmd = block_FifoNew();
301
    if( vlc_thread_create( p_vod, "rtsp vod thread", CommandThread,
302
                           VLC_THREAD_PRIORITY_LOW, false ) )
303 304 305 306 307 308 309
    {
        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 314 315 316 317 318
    if( p_sys )
    {
        if( p_sys->p_rtsp_host ) httpd_HostDelete( p_sys->p_rtsp_host );
        free( p_sys->psz_raw_mux );
        free( p_sys );
    }
319
    vlc_UrlClean( &url );
320

321 322 323 324 325 326 327 328 329 330 331
    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;

332
    /* Stop command thread */
333
    vlc_object_kill( p_vod );
334 335 336 337 338
    CommandPush( p_vod, RTSP_CMD_TYPE_NONE, NULL, NULL, 0.0, NULL );
    vlc_thread_join( p_vod );

    block_FifoRelease( p_sys->p_fifo_cmd );

339
    httpd_HostDelete( p_sys->p_rtsp_host );
340
    var_Destroy( p_this, "rtsp-session-timeout" );
341
    var_Destroy( p_this, "rtsp-throttle-users" );
342
    var_Destroy( p_this, "rtsp-raw-mux" );
343

344 345 346 347 348 349 350
    /* 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
351
    free( p_sys->psz_path );
352
    free( p_sys->psz_raw_mux );
353 354 355 356 357 358
    free( p_sys );
}

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

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

372
    memset( p_media, 0, sizeof(vod_media_t) );
373 374
    p_media->id = p_sys->i_media_id++;
    TAB_INIT( p_media->i_es, p_media->es );
375
    p_media->psz_mux = 0;
376
    TAB_INIT( p_media->i_rtsp, p_media->rtsp );
377
    p_media->b_raw = false;
378

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

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

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

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

405 406
    httpd_UrlCatch( p_media->p_rtsp_url, HTTPD_MSG_SETUP,
                    RtspCallback, (void*)p_media );
407 408 409 410 411 412
    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 );
413 414
    httpd_UrlCatch( p_media->p_rtsp_url, HTTPD_MSG_GETPARAMETER,
                    RtspCallback, (void*)p_media );
415 416 417 418 419
    httpd_UrlCatch( p_media->p_rtsp_url, HTTPD_MSG_TEARDOWN,
                    RtspCallback, (void*)p_media );

    p_media->p_vod = p_vod;

420
    vlc_mutex_lock( &p_sys->lock_media );
421
    TAB_APPEND( p_sys->i_media, p_sys->media, p_media );
422
    vlc_mutex_unlock( &p_sys->lock_media );
423

424
    vlc_mutex_init( &p_media->lock );
425 426 427 428 429 430 431 432 433 434 435 436
    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;
437
    p_media->i_length = input_item_GetDuration( p_item );
438

439 440 441
    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++ )
442 443 444
    {
        MediaAddES( p_vod, p_media, p_item->es[i] );
    }
445 446
    vlc_mutex_unlock( &p_item->lock );

447 448 449 450 451 452 453
    return p_media;
}

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

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

456 457 458 459 460 461 462
    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 );
463

464
    httpd_UrlDelete( p_media->p_rtsp_url );
465 466 467
    free( p_media->psz_rtsp_path );
    free( p_media->psz_rtsp_control_v6 );
    free( p_media->psz_rtsp_control_v4 );
468

469 470 471
    while( p_media->i_es )
        MediaDelES( p_vod, p_media, &p_media->es[0]->fmt );
    TAB_CLEAN( p_media->i_es, p_media->es );
472 473

    vlc_mutex_destroy( &p_media->lock );
474

475 476 477 478 479
    free( p_media->psz_session_name );
    free( p_media->psz_session_description );
    free( p_media->psz_session_url );
    free( p_media->psz_session_email );
    free( p_media->psz_mux );
480 481 482 483 484 485
    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) );
486
    char *psz_urlc;
487

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

491
    free( p_media->psz_mux );
492
    p_media->psz_mux = NULL;
493 494

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

500
    switch( p_fmt->i_codec )
501
    {
502 503 504 505 506
        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;
            }
507
            else if( p_fmt->audio.i_channels == 2 &&
508
                     p_fmt->audio.i_rate == 44100 )
509 510 511 512 513 514 515 516 517 518 519 520
            {
                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', ' ', ' ' ):
521
            p_es->i_payload_type = p_media->i_payload_type++;
522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541
            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;
542 543 544
        case VLC_FOURCC( 'h', '2', '6', '4' ):
            p_es->i_payload_type = p_media->i_payload_type++;
            p_es->psz_rtpmap = strdup( "H264/90000" );
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 585 586
            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 )
587
                    if( asprintf( &p_es->psz_fmtp,
588 589 590 591
                                  "packetization-mode=1;profile-level-id=%s;"
                                  "sprop-parameter-sets=%s,%s;", hexa, p_64_sps,
                                  p_64_pps ) < 0 )
                        return VLC_ENOMEM;
592 593
                free( p_64_sps );
                free( p_64_pps );
594 595 596
            }
            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
    free( p_es->psz_rtpmap );
    free( p_es->psz_fmtp );
745
    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
/* */
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 );
853 854
        free( cmd.psz_session );
        free( cmd.psz_arg );
855 856 857
    }
}

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

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

869 870
    p_rtsp->psz_session = psz_session;
    TAB_APPEND( p_media->i_rtsp, p_media->rtsp, p_rtsp );
871

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

876
    return p_rtsp;
877 878
}

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

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

    return NULL;
}

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

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

905
    TAB_REMOVE( p_media->i_rtsp, p_media->rtsp, p_rtsp );
906

907 908
    free( p_rtsp->psz_session );
    free( p_rtsp );
909 910 911
}

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

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

926
    msg_Dbg( p_vod, "RtspCallback query: type=%d", query->i_type );
927 928 929 930

    answer->i_proto   = HTTPD_PROTO_RTSP;
    answer->i_version = query->i_version;
    answer->i_type    = HTTPD_MSG_ANSWER;
931
    answer->i_body    = 0;
932
    answer->p_body    = NULL;
933 934 935

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

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

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

964 965 966 967 968 969 970 971 972
                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",
973
                         ip, i_port );
974 975 976 977

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

                    p_rtsp = RtspClientNew( p_media, psz_new );
992 993 994 995 996 997 998
                    if( !p_rtsp )
                    {
                        answer->i_status = 454;
                        answer->i_body = 0;
                        answer->p_body = NULL;
                        break;
                    }
999 1000 1001 1002 1003 1004 1005 1006 1007 1008 1009 1010 1011 1012 1013 1014 1015
                }
                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;

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

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

        case HTTPD_MSG_PLAY:
        {
1050
            char *psz_output, ip[NI_MAXNUMERICHOST];
1051
            int i, i_port_audio = 0, i_port_video = 0;
1052 1053

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

1061 1062
            if( !psz_session )
                psz_session = httpd_MsgGet( query, "Session" );
1063 1064
            msg_Dbg( p_vod, "HTTPD_MSG_PLAY for session: %s", psz_session );

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

1074 1075
            if( p_rtsp->b_playing )
            {
1076 1077
                const char *psz_position = httpd_MsgGet( query, "Range" );
                const char *psz_scale = httpd_MsgGet( query, "Scale" );
1078
                if( psz_position )
1079
                    psz_position = strstr( psz_position, "npt=" );
1080
                if( psz_position && !psz_scale )
1081 1082 1083 1084 1085 1086 1087 1088 1089 1090 1091
                {
                    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;
1092 1093
                        CommandPush( p_vod, RTSP_CMD_TYPE_SEEK, p_media,
                                     psz_session, f_pos, NULL );
1094 1095 1096
                    }
                    break;
                }
1097 1098 1099 1100 1101 1102 1103 1104 1105 1106 1107 1108
                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 );
1109
                            CommandPush( p_vod, RTSP_CMD_TYPE_REWIND, p_media,
1110
                                         psz_session, f_scale, NULL );
1111 1112 1113
                        }
                        else if(psz_scale[0] != '1' ) /* fast-forward */
                        {
1114
                            msg_Dbg( p_vod, "fastforward request: %s",
1115 1116 1117
                                     psz_scale );
                            CommandPush( p_vod, RTSP_CMD_TYPE_FORWARD, p_media,
                                         psz_session, f_scale, NULL );
1118 1119
                        }

1120
                        if( p_rtsp->b_paused == true )
1121
                        {
1122
                            p_rtsp->b_paused = false;
1123 1124
                            CommandPush( p_vod, RTSP_CMD_TYPE_PAUSE, p_media,
                                         psz_session, 0, NULL );
1125 1126 1127 1128
                        }
                    }
                    break;
                }
1129 1130
            }

1131 1132
            if( p_rtsp->b_playing && p_rtsp->b_paused )
            {
1133 1134
                CommandPush( p_vod, RTSP_CMD_TYPE_PAUSE, p_media,
                             psz_session, 0, NULL );
1135
                p_rtsp->b_paused = false;
1136 1137 1138 1139
                break;
            }
            else if( p_rtsp->b_playing ) break;

1140
            if( httpd_ClientIP( cl, ip ) == NULL ) break;
1141

1142
            p_rtsp->b_playing = true;
1143 1144

            /* FIXME for != 1 video and 1 audio */
1145
            for( i = 0; i < p_rtsp->i_es; i++ )
1146
            {
1147 1148 1149 1150
                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;
1151
            }
1152

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

1178 1179
            CommandPush( p_vod, RTSP_CMD_TYPE_PLAY, p_media, psz_session,
                         0, psz_output );
1180
            free( psz_output );
1181 1182 1183
            break;
        }

1184 1185 1186 1187 1188 1189 1190 1191
        case HTTPD_MSG_DESCRIBE:
        {
            char *psz_sdp =
                SDPGenerate( p_media, cl );

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

                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;
        }

1207 1208 1209
        case HTTPD_MSG_PAUSE:
            psz_session = httpd_MsgGet( query, "Session" );
            msg_Dbg( p_vod, "HTTPD_MSG_PAUSE for session: %s", psz_session );
1210

1211 1212 1213
            p_rtsp = RtspClientGet( p_media, psz_session );
            if( !p_rtsp ) break;

1214 1215
            CommandPush( p_vod, RTSP_CMD_TYPE_PAUSE, p_media, psz_session,
                         0, NULL );
1216
            p_rtsp->b_paused = true;
1217 1218 1219 1220 1221

            answer->i_status = 200;
            answer->i_body = 0;
            answer->p_body = NULL;
            break;
1222 1223 1224 1225 1226 1227 1228 1229 1230 1231

        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);

1232 1233
            p_rtsp = RtspClientGet( p_media, psz_session );
            if( !p_rtsp ) break;
1234

1235 1236
            CommandPush( p_vod, RTSP_CMD_TYPE_STOP, p_media, psz_session,
                         0, NULL );
1237
            RtspClientDel( p_media, p_rtsp );
1238 1239
            break;

1240 1241 1242 1243 1244 1245
        case HTTPD_MSG_GETPARAMETER:
            answer->i_status = 200;
            answer->i_body = 0;
            answer->p_body = NULL;
            break;