rtsp.c 53.2 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
    if( !p_media )
        return NULL;

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

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

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

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

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

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

    p_media->p_vod = p_vod;

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

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

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

445 446 447 448 449 450 451
    return p_media;
}

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

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

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

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

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

    vlc_mutex_destroy( &p_media->lock );
472

473 474 475 476 477
    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 );
478 479 480 481 482 483
    free( p_media );
}

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

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

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

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

498
    switch( p_fmt->i_codec )
499
    {
500 501 502 503 504
        case VLC_FOURCC( 's', '1', '6', 'b' ):
            if( p_fmt->audio.i_channels == 1 && p_fmt->audio.i_rate == 44100 )
            {
                p_es->i_payload_type = 11;
            }
505
            else if( p_fmt->audio.i_channels == 2 &&
506
                     p_fmt->audio.i_rate == 44100 )
507 508 509 510 511 512 513 514 515 516 517 518
            {
                p_es->i_payload_type = 10;
            }
            else
            {
                p_es->i_payload_type = p_media->i_payload_type++;
            }
            p_es->psz_rtpmap = malloc( strlen( "L16/*/*" ) + 20+1 );
            sprintf( p_es->psz_rtpmap, "L16/%d/%d", p_fmt->audio.i_rate,
                    p_fmt->audio.i_channels );
            break;
        case VLC_FOURCC( 'u', '8', ' ', ' ' ):
519
            p_es->i_payload_type = p_media->i_payload_type++;
520 521 522 523 524
            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' ):
525
        case VLC_FOURCC( 'm', 'p', '3', ' ' ):
526
            p_es->i_payload_type = 14;
527
            asprintf( &p_es->psz_rtpmap, "MPA/%d", p_fmt->audio.i_rate );
528 529 530 531 532 533 534
            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++;
535
            asprintf( &p_es->psz_rtpmap, "ac3/%d", p_fmt->audio.i_rate );
536 537 538 539 540
            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;
541 542 543
        case VLC_FOURCC( 'h', '2', '6', '4' ):
            p_es->i_payload_type = p_media->i_payload_type++;
            p_es->psz_rtpmap = strdup( "H264/90000" );
544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585
            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 )
586
                    if( asprintf( &p_es->psz_fmtp,
587 588 589 590
                                  "packetization-mode=1;profile-level-id=%s;"
                                  "sprop-parameter-sets=%s,%s;", hexa, p_64_sps,
                                  p_64_pps ) < 0 )
                        return VLC_ENOMEM;
591 592
                free( p_64_sps );
                free( p_64_pps );
593 594 595
            }
            if( !p_es->psz_fmtp )
                p_es->psz_fmtp = strdup( "packetization-mode=1" );
596
            break;
597 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
        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' ):
627
            p_media->psz_mux = strdup("ts");
628 629 630 631
            p_es->i_payload_type = 33;
            p_es->psz_rtpmap = strdup( "MP2T/90000" );
            break;
        case VLC_FOURCC( 'm', 'p', '2', 'p' ):
632
            p_media->psz_mux = strdup("ps");
633 634 635 636 637 638 639 640 641 642 643 644 645 646 647
            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;
648

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

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

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

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

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

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

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

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

716 717 718 719 720
    return VLC_SUCCESS;
}

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

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

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

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

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

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

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

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

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

875
    return p_rtsp;
876 877
}

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

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

    return NULL;
}

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

1141
            p_rtsp->b_playing = true;
1142 1143

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

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

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

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

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

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

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

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

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

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

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