~ubuntu-branches/ubuntu/maverick/vlc/maverick

« back to all changes in this revision

Viewing changes to modules/stream_out/rtsp.c

  • Committer: Bazaar Package Importer
  • Author(s): Reinhard Tartler
  • Date: 2008-09-17 21:56:14 UTC
  • mfrom: (1.1.17 upstream)
  • Revision ID: james.westby@ubuntu.com-20080917215614-tj0vx8xzd57e52t8
Tags: 0.9.2-1ubuntu1
* New Upstream Release, exception granted by
    - dktrkranz, norsetto, Hobbsee (via irc). LP: #270404

Changes done in ubuntu:

* add libxul-dev to build-depends
* make sure that vlc is build against libxul in configure. This doesn't
  change anything in the package, but makes it more robust if building
  in an 'unclean' chroot or when modifying the package.
* debian/control: make Vcs-* fields point to the motumedia branch
* add libx264-dev and libass-dev to build-depends
  LP: #210354, #199870
* actually enable libass support by passing --enable-libass to configure
* enable libdca: add libdca-dev to build depends and --enable-libdca
* install the x264 plugin.

Changes already in the pkg-multimedia branch in debian:

* don't install usr/share/vlc/mozilla in debian/mozilla-plugin-vlc.install  
* new upstream .desktop file now registers flash video mimetype LP: #261567
* add Xb-Npp-Applications to mozilla-plugin-vlc
* remove duplicate entries in debian/vlc-nox.install

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/*****************************************************************************
 
2
 * rtsp.c: RTSP support for RTP stream output module
 
3
 *****************************************************************************
 
4
 * Copyright (C) 2003-2004 the VideoLAN team
 
5
 * Copyright © 2007 Rémi Denis-Courmont
 
6
 *
 
7
 * $Id$
 
8
 *
 
9
 * Authors: Laurent Aimar <fenrir@via.ecp.fr>
 
10
 *
 
11
 * This program is free software; you can redistribute it and/or modify
 
12
 * it under the terms of the GNU General Public License as published by
 
13
 * the Free Software Foundation; either version 2 of the License, or
 
14
 * (at your option) any later version.
 
15
 *
 
16
 * This program is distributed in the hope that it will be useful,
 
17
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 
18
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 
19
 * GNU General Public License for more details.
 
20
 *
 
21
 * You should have received a copy of the GNU General Public License
 
22
 * along with this program; if not, write to the Free Software
 
23
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
 
24
 *****************************************************************************/
 
25
 
 
26
/*****************************************************************************
 
27
 * Preamble
 
28
 *****************************************************************************/
 
29
#ifdef HAVE_CONFIG_H
 
30
# include "config.h"
 
31
#endif
 
32
 
 
33
#include <vlc_common.h>
 
34
#include <vlc_sout.h>
 
35
 
 
36
#include <vlc_httpd.h>
 
37
#include <vlc_url.h>
 
38
#include <vlc_network.h>
 
39
#include <vlc_rand.h>
 
40
#include <assert.h>
 
41
#include <errno.h>
 
42
#include <stdlib.h>
 
43
 
 
44
#include "rtp.h"
 
45
 
 
46
typedef struct rtsp_session_t rtsp_session_t;
 
47
 
 
48
struct rtsp_stream_t
 
49
{
 
50
    vlc_mutex_t     lock;
 
51
    sout_stream_t  *owner;
 
52
    httpd_host_t   *host;
 
53
    httpd_url_t    *url;
 
54
    char           *psz_path;
 
55
    const char     *track_fmt;
 
56
    unsigned        port;
 
57
 
 
58
    int             sessionc;
 
59
    rtsp_session_t **sessionv;
 
60
};
 
61
 
 
62
 
 
63
static int  RtspCallback( httpd_callback_sys_t *p_args,
 
64
                          httpd_client_t *cl, httpd_message_t *answer,
 
65
                          const httpd_message_t *query );
 
66
static int  RtspCallbackId( httpd_callback_sys_t *p_args,
 
67
                            httpd_client_t *cl, httpd_message_t *answer,
 
68
                            const httpd_message_t *query );
 
69
static void RtspClientDel( rtsp_stream_t *rtsp, rtsp_session_t *session );
 
70
 
 
71
rtsp_stream_t *RtspSetup( sout_stream_t *p_stream, const vlc_url_t *url )
 
72
{
 
73
    rtsp_stream_t *rtsp = malloc( sizeof( *rtsp ) );
 
74
 
 
75
    if( rtsp == NULL || ( url->i_port > 99999 ) )
 
76
    {
 
77
        free( rtsp );
 
78
        return NULL;
 
79
    }
 
80
 
 
81
    rtsp->owner = p_stream;
 
82
    rtsp->sessionc = 0;
 
83
    rtsp->sessionv = NULL;
 
84
    rtsp->host = NULL;
 
85
    rtsp->url = NULL;
 
86
    rtsp->psz_path = NULL;
 
87
    vlc_mutex_init( &rtsp->lock );
 
88
 
 
89
    rtsp->port = (url->i_port > 0) ? url->i_port : 554;
 
90
    rtsp->psz_path = strdup( ( url->psz_path != NULL ) ? url->psz_path : "/" );
 
91
    if( rtsp->psz_path == NULL )
 
92
        goto error;
 
93
 
 
94
    assert( strlen( rtsp->psz_path ) > 0 );
 
95
    if( rtsp->psz_path[strlen( rtsp->psz_path ) - 1] == '/' )
 
96
        rtsp->track_fmt = "%strackID=%u";
 
97
    else
 
98
        rtsp->track_fmt = "%s/trackID=%u";
 
99
 
 
100
    msg_Dbg( p_stream, "RTSP stream: host %s port %d at %s",
 
101
             url->psz_host, rtsp->port, rtsp->psz_path );
 
102
 
 
103
    rtsp->host = httpd_HostNew( VLC_OBJECT(p_stream), url->psz_host,
 
104
                                rtsp->port );
 
105
    if( rtsp->host == NULL )
 
106
        goto error;
 
107
 
 
108
    rtsp->url = httpd_UrlNewUnique( rtsp->host, rtsp->psz_path,
 
109
                                    NULL, NULL, NULL );
 
110
    if( rtsp->url == NULL )
 
111
        goto error;
 
112
 
 
113
    httpd_UrlCatch( rtsp->url, HTTPD_MSG_DESCRIBE, RtspCallback, (void*)rtsp );
 
114
    httpd_UrlCatch( rtsp->url, HTTPD_MSG_SETUP,    RtspCallback, (void*)rtsp );
 
115
    httpd_UrlCatch( rtsp->url, HTTPD_MSG_PLAY,     RtspCallback, (void*)rtsp );
 
116
    httpd_UrlCatch( rtsp->url, HTTPD_MSG_PAUSE,    RtspCallback, (void*)rtsp );
 
117
    httpd_UrlCatch( rtsp->url, HTTPD_MSG_GETPARAMETER, RtspCallback,
 
118
                    (void*)rtsp );
 
119
    httpd_UrlCatch( rtsp->url, HTTPD_MSG_TEARDOWN, RtspCallback, (void*)rtsp );
 
120
    return rtsp;
 
121
 
 
122
error:
 
123
    RtspUnsetup( rtsp );
 
124
    return NULL;
 
125
}
 
126
 
 
127
 
 
128
void RtspUnsetup( rtsp_stream_t *rtsp )
 
129
{
 
130
    while( rtsp->sessionc > 0 )
 
131
        RtspClientDel( rtsp, rtsp->sessionv[0] );
 
132
 
 
133
    if( rtsp->url )
 
134
        httpd_UrlDelete( rtsp->url );
 
135
 
 
136
    if( rtsp->host )
 
137
        httpd_HostDelete( rtsp->host );
 
138
 
 
139
    free( rtsp->psz_path );
 
140
    vlc_mutex_destroy( &rtsp->lock );
 
141
 
 
142
    free( rtsp );
 
143
}
 
144
 
 
145
 
 
146
struct rtsp_stream_id_t
 
147
{
 
148
    rtsp_stream_t    *stream;
 
149
    sout_stream_id_t *sout_id;
 
150
    httpd_url_t      *url;
 
151
    const char       *dst;
 
152
    int               ttl;
 
153
    uint32_t          ssrc;
 
154
    uint16_t          loport, hiport;
 
155
};
 
156
 
 
157
 
 
158
typedef struct rtsp_strack_t rtsp_strack_t;
 
159
 
 
160
/* For unicast streaming */
 
161
struct rtsp_session_t
 
162
{
 
163
    rtsp_stream_t *stream;
 
164
    uint64_t       id;
 
165
 
 
166
    /* output (id-access) */
 
167
    int            trackc;
 
168
    rtsp_strack_t *trackv;
 
169
};
 
170
 
 
171
 
 
172
/* Unicast session track */
 
173
struct rtsp_strack_t
 
174
{
 
175
    sout_stream_id_t  *id;
 
176
    int                fd;
 
177
    bool         playing;
 
178
};
 
179
 
 
180
 
 
181
rtsp_stream_id_t *RtspAddId( rtsp_stream_t *rtsp, sout_stream_id_t *sid,
 
182
                             unsigned num, uint32_t ssrc,
 
183
                             /* Multicast stuff - TODO: cleanup */
 
184
                             const char *dst, int ttl,
 
185
                             unsigned loport, unsigned hiport )
 
186
{
 
187
    char urlbuf[sizeof( "/trackID=123" ) + strlen( rtsp->psz_path )];
 
188
    rtsp_stream_id_t *id = malloc( sizeof( *id ) );
 
189
    httpd_url_t *url;
 
190
 
 
191
    if( id == NULL )
 
192
        return NULL;
 
193
 
 
194
    id->stream = rtsp;
 
195
    id->sout_id = sid;
 
196
    id->ssrc = ssrc;
 
197
    /* TODO: can we assume that this need not be strdup'd? */
 
198
    id->dst = dst;
 
199
    if( id->dst != NULL )
 
200
    {
 
201
        id->ttl = ttl;
 
202
        id->loport = loport;
 
203
        id->hiport = hiport;
 
204
    }
 
205
 
 
206
    /* FIXME: num screws up if any ES has been removed and re-added */
 
207
    snprintf( urlbuf, sizeof( urlbuf ), rtsp->track_fmt, rtsp->psz_path,
 
208
              num );
 
209
    msg_Dbg( rtsp->owner, "RTSP: adding %s", urlbuf );
 
210
    url = id->url = httpd_UrlNewUnique( rtsp->host, urlbuf, NULL, NULL, NULL );
 
211
 
 
212
    if( url == NULL )
 
213
    {
 
214
        free( id );
 
215
        return NULL;
 
216
    }
 
217
 
 
218
    httpd_UrlCatch( url, HTTPD_MSG_DESCRIBE, RtspCallbackId, (void *)id );
 
219
    httpd_UrlCatch( url, HTTPD_MSG_SETUP,    RtspCallbackId, (void *)id );
 
220
    httpd_UrlCatch( url, HTTPD_MSG_PLAY,     RtspCallbackId, (void *)id );
 
221
    httpd_UrlCatch( url, HTTPD_MSG_PAUSE,    RtspCallbackId, (void *)id );
 
222
    httpd_UrlCatch( url, HTTPD_MSG_GETPARAMETER, RtspCallbackId, (void *)id );
 
223
    httpd_UrlCatch( url, HTTPD_MSG_TEARDOWN, RtspCallbackId, (void *)id );
 
224
 
 
225
    return id;
 
226
}
 
227
 
 
228
 
 
229
void RtspDelId( rtsp_stream_t *rtsp, rtsp_stream_id_t *id )
 
230
{
 
231
    vlc_mutex_lock( &rtsp->lock );
 
232
    for( int i = 0; i < rtsp->sessionc; i++ )
 
233
    {
 
234
        rtsp_session_t *ses = rtsp->sessionv[i];
 
235
 
 
236
        for( int j = 0; j < ses->trackc; j++ )
 
237
        {
 
238
            if( ses->trackv[j].id == id->sout_id )
 
239
            {
 
240
                rtsp_strack_t *tr = ses->trackv + j;
 
241
                net_Close( tr->fd );
 
242
                REMOVE_ELEM( ses->trackv, ses->trackc, j );
 
243
            }
 
244
        }
 
245
    }
 
246
 
 
247
    vlc_mutex_unlock( &rtsp->lock );
 
248
    httpd_UrlDelete( id->url );
 
249
    free( id );
 
250
}
 
251
 
 
252
 
 
253
/** rtsp must be locked */
 
254
static
 
255
rtsp_session_t *RtspClientNew( rtsp_stream_t *rtsp )
 
256
{
 
257
    rtsp_session_t *s = malloc( sizeof( *s ) );
 
258
    if( s == NULL )
 
259
        return NULL;
 
260
 
 
261
    s->stream = rtsp;
 
262
    vlc_rand_bytes (&s->id, sizeof (s->id));
 
263
    s->trackc = 0;
 
264
    s->trackv = NULL;
 
265
 
 
266
    TAB_APPEND( rtsp->sessionc, rtsp->sessionv, s );
 
267
 
 
268
    return s;
 
269
}
 
270
 
 
271
 
 
272
/** rtsp must be locked */
 
273
static
 
274
rtsp_session_t *RtspClientGet( rtsp_stream_t *rtsp, const char *name )
 
275
{
 
276
    char *end;
 
277
    uint64_t id;
 
278
    int i;
 
279
 
 
280
    if( name == NULL )
 
281
        return NULL;
 
282
 
 
283
    errno = 0;
 
284
    id = strtoull( name, &end, 0x10 );
 
285
    if( errno || *end )
 
286
        return NULL;
 
287
 
 
288
    /* FIXME: use a hash/dictionary */
 
289
    for( i = 0; i < rtsp->sessionc; i++ )
 
290
    {
 
291
        if( rtsp->sessionv[i]->id == id )
 
292
            return rtsp->sessionv[i];
 
293
    }
 
294
    return NULL;
 
295
}
 
296
 
 
297
 
 
298
/** rtsp must be locked */
 
299
static
 
300
void RtspClientDel( rtsp_stream_t *rtsp, rtsp_session_t *session )
 
301
{
 
302
    int i;
 
303
    TAB_REMOVE( rtsp->sessionc, rtsp->sessionv, session );
 
304
 
 
305
    for( i = 0; i < session->trackc; i++ )
 
306
        rtp_del_sink( session->trackv[i].id, session->trackv[i].fd );
 
307
 
 
308
    free( session->trackv );
 
309
    free( session );
 
310
}
 
311
 
 
312
 
 
313
/** Finds the next transport choice */
 
314
static inline const char *transport_next( const char *str )
 
315
{
 
316
    /* Looks for comma */
 
317
    str = strchr( str, ',' );
 
318
    if( str == NULL )
 
319
        return NULL; /* No more transport options */
 
320
 
 
321
    str++; /* skips comma */
 
322
    while( strchr( "\r\n\t ", *str ) )
 
323
        str++;
 
324
 
 
325
    return (*str) ? str : NULL;
 
326
}
 
327
 
 
328
 
 
329
/** Finds the next transport parameter */
 
330
static inline const char *parameter_next( const char *str )
 
331
{
 
332
    while( strchr( ",;", *str ) == NULL )
 
333
        str++;
 
334
 
 
335
    return (*str == ';') ? (str + 1) : NULL;
 
336
}
 
337
 
 
338
 
 
339
/** RTSP requests handler
 
340
 * @param id selected track for non-aggregate URLs,
 
341
 *           NULL for aggregate URLs
 
342
 */
 
343
static int RtspHandler( rtsp_stream_t *rtsp, rtsp_stream_id_t *id,
 
344
                        httpd_client_t *cl,
 
345
                        httpd_message_t *answer,
 
346
                        const httpd_message_t *query )
 
347
{
 
348
    sout_stream_t *p_stream = rtsp->owner;
 
349
    char psz_sesbuf[17];
 
350
    const char *psz_session = NULL, *psz;
 
351
    char control[sizeof("rtsp://[]:12345") + NI_MAXNUMERICHOST
 
352
                  + strlen( rtsp->psz_path )];
 
353
    time_t now;
 
354
 
 
355
    time (&now);
 
356
 
 
357
    if( answer == NULL || query == NULL || cl == NULL )
 
358
        return VLC_SUCCESS;
 
359
    else
 
360
    {
 
361
        /* Build self-referential control URL */
 
362
        char ip[NI_MAXNUMERICHOST], *ptr;
 
363
 
 
364
        httpd_ServerIP( cl, ip );
 
365
        ptr = strchr( ip, '%' );
 
366
        if( ptr != NULL )
 
367
            *ptr = '\0';
 
368
 
 
369
        if( strchr( ip, ':' ) != NULL )
 
370
            sprintf( control, "rtsp://[%s]:%u%s", ip, rtsp->port,
 
371
                     rtsp->psz_path );
 
372
        else
 
373
            sprintf( control, "rtsp://%s:%u%s", ip, rtsp->port,
 
374
                     rtsp->psz_path );
 
375
    }
 
376
 
 
377
    /* */
 
378
    answer->i_proto = HTTPD_PROTO_RTSP;
 
379
    answer->i_version= 0;
 
380
    answer->i_type   = HTTPD_MSG_ANSWER;
 
381
    answer->i_body = 0;
 
382
    answer->p_body = NULL;
 
383
 
 
384
    httpd_MsgAdd( answer, "Server", "%s", PACKAGE_STRING );
 
385
 
 
386
    /* Date: is always allowed, and sometimes mandatory with RTSP/2.0. */
 
387
    struct tm ut;
 
388
    if (gmtime_r (&now, &ut) != NULL)
 
389
    {   /* RFC1123 format, GMT is mandatory */
 
390
        static const char wdays[7][4] = {
 
391
            "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat" };
 
392
        static const char mons[12][4] = {
 
393
            "Jan", "Feb", "Mar", "Apr", "May", "Jun",
 
394
            "Jul", "Aug", "Sep", "Oct", "Nov", "Dec" };
 
395
        httpd_MsgAdd (answer, "Date", "%s, %02u %s %04u %02u:%02u:%02u GMT",
 
396
                      wdays[ut.tm_wday], ut.tm_mday, mons[ut.tm_mon],
 
397
                      1900 + ut.tm_year, ut.tm_hour, ut.tm_min, ut.tm_sec);
 
398
    }
 
399
 
 
400
    if( query->i_proto != HTTPD_PROTO_RTSP )
 
401
    {
 
402
        answer->i_status = 505;
 
403
    }
 
404
    else
 
405
    if( httpd_MsgGet( query, "Require" ) != NULL )
 
406
    {
 
407
        answer->i_status = 551;
 
408
        httpd_MsgAdd( answer, "Unsupported", "%s",
 
409
                      httpd_MsgGet( query, "Require" ) );
 
410
    }
 
411
    else
 
412
    switch( query->i_type )
 
413
    {
 
414
        case HTTPD_MSG_DESCRIBE:
 
415
        {   /* Aggregate-only */
 
416
            if( id != NULL )
 
417
            {
 
418
                answer->i_status = 460;
 
419
                break;
 
420
            }
 
421
 
 
422
            answer->i_status = 200;
 
423
            httpd_MsgAdd( answer, "Content-Type",  "%s", "application/sdp" );
 
424
            httpd_MsgAdd( answer, "Content-Base",  "%s", control );
 
425
            answer->p_body = (uint8_t *)SDPGenerate( rtsp->owner, control );
 
426
            if( answer->p_body != NULL )
 
427
                answer->i_body = strlen( (char *)answer->p_body );
 
428
            else
 
429
                answer->i_status = 500;
 
430
            break;
 
431
        }
 
432
 
 
433
        case HTTPD_MSG_SETUP:
 
434
            /* Non-aggregate-only */
 
435
            if( id == NULL )
 
436
            {
 
437
                answer->i_status = 459;
 
438
                break;
 
439
            }
 
440
 
 
441
            psz_session = httpd_MsgGet( query, "Session" );
 
442
            answer->i_status = 461;
 
443
 
 
444
            for( const char *tpt = httpd_MsgGet( query, "Transport" );
 
445
                 tpt != NULL;
 
446
                 tpt = transport_next( tpt ) )
 
447
            {
 
448
                bool b_multicast = true, b_unsupp = false;
 
449
                unsigned loport = 5004, hiport = 5005; /* from RFC3551 */
 
450
 
 
451
                /* Check transport protocol. */
 
452
                /* Currently, we only support RTP/AVP over UDP */
 
453
                if( strncmp( tpt, "RTP/AVP", 7 ) )
 
454
                    continue;
 
455
                tpt += 7;
 
456
                if( strncmp( tpt, "/UDP", 4 ) == 0 )
 
457
                    tpt += 4;
 
458
                if( strchr( ";,", *tpt ) == NULL )
 
459
                    continue;
 
460
 
 
461
                /* Parse transport options */
 
462
                for( const char *opt = parameter_next( tpt );
 
463
                     opt != NULL;
 
464
                     opt = parameter_next( opt ) )
 
465
                {
 
466
                    if( strncmp( opt, "multicast", 9 ) == 0)
 
467
                        b_multicast = true;
 
468
                    else
 
469
                    if( strncmp( opt, "unicast", 7 ) == 0 )
 
470
                        b_multicast = false;
 
471
                    else
 
472
                    if( sscanf( opt, "client_port=%u-%u", &loport, &hiport )
 
473
                                == 2 )
 
474
                        ;
 
475
                    else
 
476
                    if( strncmp( opt, "mode=", 5 ) == 0 )
 
477
                    {
 
478
                        if( strncasecmp( opt + 5, "play", 4 )
 
479
                         && strncasecmp( opt + 5, "\"PLAY\"", 6 ) )
 
480
                        {
 
481
                            /* Not playing?! */
 
482
                            b_unsupp = true;
 
483
                            break;
 
484
                        }
 
485
                    }
 
486
                    else
 
487
                    if( strncmp( opt,"destination=", 12 ) == 0 )
 
488
                    {
 
489
                        answer->i_status = 403;
 
490
                        b_unsupp = true;
 
491
                    }
 
492
                    else
 
493
                    {
 
494
                    /*
 
495
                     * Every other option is unsupported:
 
496
                     *
 
497
                     * "source" and "append" are invalid (server-only);
 
498
                     * "ssrc" also (as clarified per RFC2326bis).
 
499
                     *
 
500
                     * For multicast, "port", "layers", "ttl" are set by the
 
501
                     * stream output configuration.
 
502
                     *
 
503
                     * For unicast, we want to decide "server_port" values.
 
504
                     *
 
505
                     * "interleaved" is not implemented.
 
506
                     */
 
507
                        b_unsupp = true;
 
508
                        break;
 
509
                    }
 
510
                }
 
511
 
 
512
                if( b_unsupp )
 
513
                    continue;
 
514
 
 
515
                if( b_multicast )
 
516
                {
 
517
                    const char *dst = id->dst;
 
518
                    if( dst == NULL )
 
519
                        continue;
 
520
 
 
521
                    if( psz_session == NULL )
 
522
                    {
 
523
                        /* Create a dummy session ID */
 
524
                        snprintf( psz_sesbuf, sizeof( psz_sesbuf ), "%d",
 
525
                                  rand() );
 
526
                        psz_session = psz_sesbuf;
 
527
                    }
 
528
                    answer->i_status = 200;
 
529
 
 
530
                    httpd_MsgAdd( answer, "Transport",
 
531
                                  "RTP/AVP/UDP;destination=%s;port=%u-%u;"
 
532
                                  "ttl=%d;mode=play",
 
533
                                  dst, id->loport, id->hiport,
 
534
                                  ( id->ttl > 0 ) ? id->ttl : 1 );
 
535
                }
 
536
                else
 
537
                {
 
538
                    char ip[NI_MAXNUMERICHOST], src[NI_MAXNUMERICHOST];
 
539
                    rtsp_session_t *ses = NULL;
 
540
                    rtsp_strack_t track = { id->sout_id, -1, false };
 
541
                    int sport;
 
542
 
 
543
                    if( httpd_ClientIP( cl, ip ) == NULL )
 
544
                    {
 
545
                        answer->i_status = 500;
 
546
                        continue;
 
547
                    }
 
548
 
 
549
                    track.fd = net_ConnectDgram( p_stream, ip, loport, -1,
 
550
                                                 IPPROTO_UDP );
 
551
                    if( track.fd == -1 )
 
552
                    {
 
553
                        msg_Err( p_stream,
 
554
                                 "cannot create RTP socket for %s port %u",
 
555
                                 ip, loport );
 
556
                        answer->i_status = 500;
 
557
                        continue;
 
558
                    }
 
559
 
 
560
                    net_GetSockAddress( track.fd, src, &sport );
 
561
 
 
562
                    vlc_mutex_lock( &rtsp->lock );
 
563
                    if( psz_session == NULL )
 
564
                    {
 
565
                        ses = RtspClientNew( rtsp );
 
566
                        snprintf( psz_sesbuf, sizeof( psz_sesbuf ), "%"PRIx64,
 
567
                                  ses->id );
 
568
                        psz_session = psz_sesbuf;
 
569
                    }
 
570
                    else
 
571
                    {
 
572
                        /* FIXME: we probably need to remove an access out,
 
573
                         * if there is already one for the same ID */
 
574
                        ses = RtspClientGet( rtsp, psz_session );
 
575
                        if( ses == NULL )
 
576
                        {
 
577
                            answer->i_status = 454;
 
578
                            vlc_mutex_unlock( &rtsp->lock );
 
579
                            continue;
 
580
                        }
 
581
                    }
 
582
 
 
583
                    INSERT_ELEM( ses->trackv, ses->trackc, ses->trackc,
 
584
                                 track );
 
585
                    vlc_mutex_unlock( &rtsp->lock );
 
586
 
 
587
                    httpd_ServerIP( cl, ip );
 
588
 
 
589
                    if( strcmp( src, ip ) )
 
590
                    {
 
591
                        /* Specify source IP if it is different from the RTSP
 
592
                         * control connection server address */
 
593
                        char *ptr = strchr( src, '%' );
 
594
                        if( ptr != NULL ) *ptr = '\0'; /* remove scope ID */
 
595
 
 
596
                        httpd_MsgAdd( answer, "Transport",
 
597
                                      "RTP/AVP/UDP;unicast;source=%s;"
 
598
                                      "client_port=%u-%u;server_port=%u-%u;"
 
599
                                      "ssrc=%08X;mode=play",
 
600
                                      src, loport, loport + 1, sport,
 
601
                                      sport + 1, id->ssrc );
 
602
                    }
 
603
                    else
 
604
                    {
 
605
                        httpd_MsgAdd( answer, "Transport",
 
606
                                      "RTP/AVP/UDP;unicast;"
 
607
                                      "client_port=%u-%u;server_port=%u-%u;"
 
608
                                      "ssrc=%08X;mode=play",
 
609
                                      loport, loport + 1, sport, sport + 1,
 
610
                                      id->ssrc );
 
611
                    }
 
612
 
 
613
                    answer->i_status = 200;
 
614
                }
 
615
                break;
 
616
            }
 
617
            break;
 
618
 
 
619
        case HTTPD_MSG_PLAY:
 
620
        {
 
621
            rtsp_session_t *ses;
 
622
            answer->i_status = 200;
 
623
 
 
624
            psz_session = httpd_MsgGet( query, "Session" );
 
625
            const char *range = httpd_MsgGet (query, "Range");
 
626
            if (range && strncmp (range, "npt=", 4))
 
627
            {
 
628
                answer->i_status = 501;
 
629
                break;
 
630
            }
 
631
 
 
632
            vlc_mutex_lock( &rtsp->lock );
 
633
            ses = RtspClientGet( rtsp, psz_session );
 
634
            if( ses != NULL )
 
635
            {
 
636
                /* FIXME: we really need to limit the number of tracks... */
 
637
                char info[ses->trackc * ( strlen( control )
 
638
                                  + sizeof("/trackID=123;seq=65535, ") ) + 1];
 
639
                size_t infolen = 0;
 
640
 
 
641
                for( int i = 0; i < ses->trackc; i++ )
 
642
                {
 
643
                    rtsp_strack_t *tr = ses->trackv + i;
 
644
                    if( ( id == NULL ) || ( tr->id == id->sout_id ) )
 
645
                    {
 
646
                        if( !tr->playing )
 
647
                        {
 
648
                            tr->playing = true;
 
649
                            rtp_add_sink( tr->id, tr->fd, false );
 
650
                        }
 
651
                        infolen += sprintf( info + infolen,
 
652
                                            "%s/trackID=%u;seq=%u, ", control,
 
653
                                            rtp_get_num( tr->id ),
 
654
                                            rtp_get_seq( tr->id ) );
 
655
                    }
 
656
                }
 
657
                if( infolen > 0 )
 
658
                {
 
659
                    info[infolen - 2] = '\0'; /* remove trailing ", " */
 
660
                    httpd_MsgAdd( answer, "RTP-Info", "%s", info );
 
661
                }
 
662
            }
 
663
            vlc_mutex_unlock( &rtsp->lock );
 
664
 
 
665
            if( httpd_MsgGet( query, "Scale" ) != NULL )
 
666
                httpd_MsgAdd( answer, "Scale", "1." );
 
667
            break;
 
668
        }
 
669
 
 
670
        case HTTPD_MSG_PAUSE:
 
671
            answer->i_status = 405;
 
672
            httpd_MsgAdd( answer, "Allow",
 
673
                          "%s, TEARDOWN, PLAY, GET_PARAMETER",
 
674
                          ( id != NULL ) ? "SETUP" : "DESCRIBE" );
 
675
            break;
 
676
 
 
677
        case HTTPD_MSG_GETPARAMETER:
 
678
            if( query->i_body > 0 )
 
679
            {
 
680
                answer->i_status = 451;
 
681
                break;
 
682
            }
 
683
 
 
684
            psz_session = httpd_MsgGet( query, "Session" );
 
685
            answer->i_status = 200;
 
686
            break;
 
687
 
 
688
        case HTTPD_MSG_TEARDOWN:
 
689
        {
 
690
            rtsp_session_t *ses;
 
691
 
 
692
            answer->i_status = 200;
 
693
 
 
694
            psz_session = httpd_MsgGet( query, "Session" );
 
695
 
 
696
            vlc_mutex_lock( &rtsp->lock );
 
697
            ses = RtspClientGet( rtsp, psz_session );
 
698
            if( ses != NULL )
 
699
            {
 
700
                if( id == NULL ) /* Delete the entire session */
 
701
                    RtspClientDel( rtsp, ses );
 
702
                else /* Delete one track from the session */
 
703
                for( int i = 0; i < ses->trackc; i++ )
 
704
                {
 
705
                    if( ses->trackv[i].id == id->sout_id )
 
706
                    {
 
707
                        rtp_del_sink( id->sout_id, ses->trackv[i].fd );
 
708
                        REMOVE_ELEM( ses->trackv, ses->trackc, i );
 
709
                    }
 
710
                }
 
711
            }
 
712
            vlc_mutex_unlock( &rtsp->lock );
 
713
            break;
 
714
        }
 
715
 
 
716
        default:
 
717
            return VLC_EGENERIC;
 
718
    }
 
719
 
 
720
    if( psz_session )
 
721
        httpd_MsgAdd( answer, "Session", "%s"/*;timeout=5*/, psz_session );
 
722
 
 
723
    httpd_MsgAdd( answer, "Content-Length", "%d", answer->i_body );
 
724
    httpd_MsgAdd( answer, "Cache-Control", "no-cache" );
 
725
 
 
726
    psz = httpd_MsgGet( query, "Cseq" );
 
727
    if( psz != NULL )
 
728
        httpd_MsgAdd( answer, "Cseq", "%s", psz );
 
729
    psz = httpd_MsgGet( query, "Timestamp" );
 
730
    if( psz != NULL )
 
731
        httpd_MsgAdd( answer, "Timestamp", "%s", psz );
 
732
 
 
733
    return VLC_SUCCESS;
 
734
}
 
735
 
 
736
 
 
737
/** Aggregate RTSP callback */
 
738
static int RtspCallback( httpd_callback_sys_t *p_args,
 
739
                         httpd_client_t *cl,
 
740
                         httpd_message_t *answer,
 
741
                         const httpd_message_t *query )
 
742
{
 
743
    return RtspHandler( (rtsp_stream_t *)p_args, NULL, cl, answer, query );
 
744
}
 
745
 
 
746
 
 
747
/** Non-aggregate RTSP callback */
 
748
static int RtspCallbackId( httpd_callback_sys_t *p_args,
 
749
                           httpd_client_t *cl,
 
750
                           httpd_message_t *answer,
 
751
                           const httpd_message_t *query )
 
752
{
 
753
    rtsp_stream_id_t *id = (rtsp_stream_id_t *)p_args;
 
754
    return RtspHandler( id->stream, id, cl, answer, query );
 
755
}