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_common.h>
34
#include <vlc_plugin.h>
Clément Stenac's avatar
Clément Stenac committed
35 36
#include <vlc_input.h>
#include <vlc_sout.h>
37
#include <vlc_block.h>
38 39 40

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

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

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

54
#define HOST_TEXT N_( "RTSP host address" )
55
#define HOST_LONGTEXT N_( \
56 57
    "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
58
    "interfaces (address 0.0.0.0), on port 554, with no path.\nTo listen " \
59
    "only on the local interface, use \"localhost\" as address." )
60

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

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

67 68 69 70 71 72 73
#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." )

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

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

95 96 97 98 99 100 101 102 103 104
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;

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

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

    int i_es;
    rtsp_client_es_t **es;
115 116 117

} rtsp_client_t;

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

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

    vod_media_t *p_media;

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

134
};
135 136 137

struct vod_media_t
{
138 139
    int id;

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

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

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

    int64_t i_sdp_id;
    int     i_sdp_version;

158
    bool b_multicast;
159

160
    vlc_mutex_t lock;
161 162 163 164

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

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

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

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

189 190
    char *psz_raw_mux;

191 192
    int i_session_timeout;

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

    /* */
    block_fifo_t *p_fifo_cmd;
201 202
};

203 204 205 206 207 208 209 210 211 212 213 214 215
/* 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;

216
static vod_media_t *MediaNew( vod_t *, const char *, input_item_t * );
217 218 219 220
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 * );

221 222 223 224
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 );

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

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

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

236 237 238 239 240 241 242 243 244 245 246 247 248
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';
}

249 250 251 252 253 254 255 256 257 258 259 260
/*****************************************************************************
 * 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 );
261
    free( psz_url );
262 263 264 265 266 267 268

    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;

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

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

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

277 278 279 280
    p_sys->p_rtsp_host =
        httpd_HostNew( VLC_OBJECT(p_vod), url.psz_host, url.i_port );
    if( !p_sys->p_rtsp_host )
    {
281
        msg_Err( p_vod, "cannot create RTSP server (%s:%i)",
282 283 284 285 286 287 288 289
                 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 );
290

291
    vlc_mutex_init( &p_sys->lock_media );
292 293 294

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

    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;

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

311 312
    return VLC_SUCCESS;

313
error:
314 315 316 317 318 319
    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 );
    }
320
    vlc_UrlClean( &url );
321

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

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

    block_FifoRelease( p_sys->p_fifo_cmd );

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

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

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

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

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

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

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

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

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

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

    p_media->p_vod = p_vod;

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

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

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

448 449 450 451 452 453 454
    return p_media;
}

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

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

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

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

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

    vlc_mutex_destroy( &p_media->lock );
475

476 477 478 479 480
    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 );
481 482 483 484 485 486
    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) );
487
    char *psz_urlc;
488

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

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

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

501
    switch( p_fmt->i_codec )
502
    {
503 504 505 506 507
        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;
            }
508
            else if( p_fmt->audio.i_channels == 2 &&
509
                     p_fmt->audio.i_rate == 44100 )
510 511 512 513 514 515 516 517 518 519 520 521
            {
                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', ' ', ' ' ):
522
            p_es->i_payload_type = p_media->i_payload_type++;
523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542
            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;
543 544 545
        case VLC_FOURCC( 'h', '2', '6', '4' ):
            p_es->i_payload_type = p_media->i_payload_type++;
            p_es->psz_rtpmap = strdup( "H264/90000" );
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 587
            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 )
588
                    if( asprintf( &p_es->psz_fmtp,
589 590 591 592
                                  "packetization-mode=1;profile-level-id=%s;"
                                  "sprop-parameter-sets=%s,%s;", hexa, p_64_sps,
                                  p_64_pps ) < 0 )
                        return VLC_ENOMEM;
593 594
                free( p_64_sps );
                free( p_64_pps );
595 596 597
            }
            if( !p_es->psz_fmtp )
                p_es->psz_fmtp = strdup( "packetization-mode=1" );
598
            break;
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 628
        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' ):
629
            p_media->psz_mux = strdup("ts");
630 631 632 633
            p_es->i_payload_type = 33;
            p_es->psz_rtpmap = strdup( "MP2T/90000" );
            break;
        case VLC_FOURCC( 'm', 'p', '2', 'p' ):
634
            p_media->psz_mux = strdup("ps");
635 636 637 638 639 640 641 642 643 644 645 646 647 648 649
            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;
650

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

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

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

    httpd_UrlCatch( p_es->p_rtsp_url, HTTPD_MSG_SETUP,
                    RtspCallbackES, (void*)p_es );
673 674 675 676 677 678
    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 );
679

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

684
#if 0
685 686 687 688 689 690 691 692 693 694 695 696 697 698 699 700 701 702 703 704 705 706
    /* 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;
    }
707 708 709 710
#else

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

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

718 719 720 721 722
    return VLC_SUCCESS;
}

static void MediaDelES( vod_t *p_vod, vod_media_t *p_media, es_format_t *p_fmt)
{
723
    media_es_t *p_es = NULL;
724 725 726 727 728 729 730 731 732 733 734 735 736
    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;
737

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

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

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

    if( p_es->p_rtsp_url ) httpd_UrlDelete( p_es->p_rtsp_url );
749
    es_format_Clean( &p_es->fmt );
750
    free( p_es );
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
/* */
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 );
854 855
        free( cmd.psz_session );
        free( cmd.psz_arg );
856 857 858
    }
}

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

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

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

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

877
    return p_rtsp;
878 879
}

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

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

    return NULL;
}

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

1143
            p_rtsp->b_playing = true;
1144 1145

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

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

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

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

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

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

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

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

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

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

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

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

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

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