sap.c 21.4 KB
Newer Older
zorglub's avatar
zorglub committed
1
2
3
/*****************************************************************************
 * sap.c : SAP announce handler
 *****************************************************************************
4
 * Copyright (C) 2002-2005 the VideoLAN team
5
 * $Id$
zorglub's avatar
zorglub committed
6
 *
7
8
 * Authors: Clément Stenac <zorglub@videolan.org>
 *          Rémi Denis-Courmont <rem # videolan.org>
zorglub's avatar
zorglub committed
9
10
11
12
13
14
15
16
17
18
19
20
21
 *
 * 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
dionoea's avatar
dionoea committed
22
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
zorglub's avatar
zorglub committed
23
24
25
26
27
 *****************************************************************************/

/*****************************************************************************
 * Preamble
 *****************************************************************************/
28
29
30

#include <vlc/vlc.h>

zorglub's avatar
zorglub committed
31
32
33
#include <stdlib.h>                                                /* free() */
#include <stdio.h>                                              /* sprintf() */
#include <string.h>                                            /* strerror() */
34
#include <ctype.h>                                  /* tolower(), isxdigit() */
zorglub's avatar
zorglub committed
35
36
37

#include <vlc/sout.h>

38
#include "network.h"
39
#include "charset.h"
zorglub's avatar
zorglub committed
40

41
/* SAP is always on that port */
zorglub's avatar
zorglub committed
42
43
44
45
46
47
#define SAP_PORT 9875

#define DEFAULT_PORT "1234"

#undef EXTRA_DEBUG

48
49
50
51
52
53
54
55
56
57
58
59
60
/* SAP Specific structures */

/* 100ms */
#define SAP_IDLE ((mtime_t)(0.100*CLOCK_FREQ))
#define SAP_MAX_BUFFER 65534
#define MIN_INTERVAL 2
#define MAX_INTERVAL 300

/* A SAP announce address. For each of these, we run the
 * control flow algorithm */
struct sap_address_t
{
    char *psz_address;
61
    char psz_machine[NI_MAXNUMERICHOST];
62
63
64
65
66
67
68
69
70
71
72
73
    int i_rfd; /* Read socket */
    int i_wfd; /* Write socket */

    /* Used for flow control */
    mtime_t t1;
    vlc_bool_t b_enabled;
    vlc_bool_t b_ready;
    int i_interval;
    int i_buff;
    int i_limit;
};

zorglub's avatar
zorglub committed
74
75
76
77
78
/*****************************************************************************
 * Local prototypes
 *****************************************************************************/
static void RunThread( vlc_object_t *p_this);
static int CalculateRate( sap_handler_t *p_sap, sap_address_t *p_address );
79
80
81
static char *SDPGenerate( sap_handler_t *p_sap,
                          const session_descriptor_t *p_session,
                          const sap_address_t *p_addr );
zorglub's avatar
zorglub committed
82
83
84
85
86
87

static int announce_SendSAPAnnounce( sap_handler_t *p_sap,
                                     sap_session_t *p_session );


static int announce_SAPAnnounceAdd( sap_handler_t *p_sap,
88
                             session_descriptor_t *p_session );
zorglub's avatar
zorglub committed
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125

static int announce_SAPAnnounceDel( sap_handler_t *p_sap,
                             session_descriptor_t *p_session );


/**
 * Create the SAP handler
 *
 * \param p_announce the parent announce_handler
 * \return the newly created SAP handler or NULL on error
 */
sap_handler_t *announce_SAPHandlerCreate( announce_handler_t *p_announce )
{
    sap_handler_t *p_sap;

    p_sap = vlc_object_create( p_announce, sizeof( sap_handler_t ) );

    if( !p_sap )
    {
        msg_Err( p_announce, "out of memory" );
        return NULL;
    }

    vlc_mutex_init( p_sap, &p_sap->object_lock );

    p_sap->pf_add = announce_SAPAnnounceAdd;
    p_sap->pf_del = announce_SAPAnnounceDel;

    p_sap->i_sessions = 0;
    p_sap->i_addresses = 0;
    p_sap->i_current_session = 0;

    p_sap->b_control = config_GetInt( p_sap, "sap-flow-control");

    if( vlc_thread_create( p_sap, "sap handler", RunThread,
                       VLC_THREAD_PRIORITY_LOW, VLC_FALSE ) )
    {
126
        msg_Dbg( p_announce, "unable to spawn SAP handler thread");
zorglub's avatar
zorglub committed
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
        free( p_sap );
        return NULL;
    };
    msg_Dbg( p_announce, "thread created, %i sessions", p_sap->i_sessions);
    return p_sap;
}

/**
 *  Destroy the SAP handler
 *  \param p_this the SAP Handler to destroy
 *  \return nothing
 */
void announce_SAPHandlerDestroy( sap_handler_t *p_sap )
{
    int i;

    vlc_mutex_destroy( &p_sap->object_lock );

    /* Free the remaining sessions */
    for( i = 0 ; i< p_sap->i_sessions ; i++)
    {
        sap_session_t *p_session = p_sap->pp_sessions[i];
zorglub's avatar
zorglub committed
149
150
        FREENULL( p_session->psz_sdp );
        FREENULL( p_session->psz_data );
zorglub's avatar
zorglub committed
151
        REMOVE_ELEM( p_sap->pp_sessions, p_sap->i_sessions , i );
zorglub's avatar
zorglub committed
152
        FREENULL( p_session );
zorglub's avatar
zorglub committed
153
154
155
156
157
158
    }

    /* Free the remaining addresses */
    for( i = 0 ; i< p_sap->i_addresses ; i++)
    {
        sap_address_t *p_address = p_sap->pp_addresses[i];
zorglub's avatar
zorglub committed
159
        FREENULL( p_address->psz_address );
zorglub's avatar
zorglub committed
160
161
162
163
164
165
166
167
168
        if( p_address->i_rfd > -1 )
        {
            net_Close( p_address->i_rfd );
        }
        if( p_address->i_wfd > -1 && p_sap->b_control )
        {
            net_Close( p_address->i_wfd );
        }
        REMOVE_ELEM( p_sap->pp_addresses, p_sap->i_addresses, i );
zorglub's avatar
zorglub committed
169
        FREENULL( p_address );
zorglub's avatar
zorglub committed
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
    }

    /* Free the structure */
    vlc_object_destroy( p_sap );
}

/**
 * main SAP handler thread
 * \param p_this the SAP Handler object
 * \return nothing
 */
static void RunThread( vlc_object_t *p_this)
{
    sap_handler_t *p_sap = (sap_handler_t*)p_this;
    sap_session_t *p_session;

    while( !p_sap->b_die )
    {
        int i;

        /* If needed, get the rate info */
        if( p_sap->b_control == VLC_TRUE )
        {
            for( i = 0 ; i< p_sap->i_addresses ; i++)
            {
                if( p_sap->pp_addresses[i]->b_enabled == VLC_TRUE )
                {
                    CalculateRate( p_sap, p_sap->pp_addresses[i] );
                }
            }
        }

        /* Find the session to announce */
        vlc_mutex_lock( &p_sap->object_lock );
        if( p_sap->i_sessions > p_sap->i_current_session + 1)
        {
            p_sap->i_current_session++;
        }
        else if( p_sap->i_sessions > 0)
        {
            p_sap->i_current_session = 0;
        }
        else
        {
            vlc_mutex_unlock( &p_sap->object_lock );
            msleep( SAP_IDLE );
            continue;
        }
        p_session = p_sap->pp_sessions[p_sap->i_current_session];
        vlc_mutex_unlock( &p_sap->object_lock );

        /* And announce it */
        if( p_session->p_address->b_enabled == VLC_TRUE &&
            p_session->p_address->b_ready == VLC_TRUE )
        {
            announce_SendSAPAnnounce( p_sap, p_session );
        }

        msleep( SAP_IDLE );
    }
}

/* Add a SAP announce */
static int announce_SAPAnnounceAdd( sap_handler_t *p_sap,
234
                             session_descriptor_t *p_session )
zorglub's avatar
zorglub committed
235
{
236
237
    int i_header_size, i;
    char *psz_head, psz_addr[NI_MAXNUMERICHOST];
238
    vlc_bool_t b_ipv6 = VLC_FALSE;
zorglub's avatar
zorglub committed
239
    sap_session_t *p_sap_session;
240
    mtime_t i_hash;
241
    struct addrinfo hints, *res;
242
    struct sockaddr_storage addr;
Rémi Denis-Courmont's avatar
Cleanup    
Rémi Denis-Courmont committed
243
    socklen_t addrlen;
zorglub's avatar
zorglub committed
244
245
246

    vlc_mutex_lock( &p_sap->object_lock );

247
    if( p_session->psz_uri == NULL )
zorglub's avatar
zorglub committed
248
    {
249
        vlc_mutex_unlock( &p_sap->object_lock );
250
        msg_Err( p_sap, "*FIXME* unexpected NULL URI for SAP announce" );
251
        msg_Err( p_sap, "This should not happen. VLC needs fixing." );
252
        return VLC_EGENERIC;
zorglub's avatar
zorglub committed
253
254
    }

255
    /* Determine SAP multicast address automatically */
256
257
258
    memset( &hints, 0, sizeof( hints ) );
    hints.ai_socktype = SOCK_DGRAM;
    hints.ai_flags = AI_NUMERICHOST;
259

260
261
    i = vlc_getaddrinfo( (vlc_object_t *)p_sap, p_session->psz_uri, 0,
                         &hints, &res );
262
    if( i )
263
    {
264
265
266
267
        vlc_mutex_unlock( &p_sap->object_lock );
        msg_Err( p_sap, "Invalid URI for SAP announce: %s: %s",
                 p_session->psz_uri, vlc_gai_strerror( i ) );
        return VLC_EGENERIC;
268
    }
269

Rémi Denis-Courmont's avatar
Cleanup    
Rémi Denis-Courmont committed
270
271
    addrlen = res->ai_addrlen;
    if ((unsigned)addrlen > sizeof (addr))
272
273
    {
        vlc_mutex_unlock( &p_sap->object_lock );
274
275
        vlc_freeaddrinfo( res );
        msg_Err( p_sap, "Unsupported address family of size %d > %u",
276
                 res->ai_addrlen, (unsigned) sizeof( addr ) );
277
278
        return VLC_EGENERIC;
    }
hartman's avatar
hartman committed
279

Rémi Denis-Courmont's avatar
Cleanup    
Rémi Denis-Courmont committed
280
281
    memcpy (&addr, res->ai_addr, addrlen);
    vlc_freeaddrinfo (res);
282

283
    switch( addr.ss_family )
284
    {
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
285
#if defined (HAVE_INET_PTON) || defined (WIN32)
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
        case AF_INET6:
        {
            /* See RFC3513 for list of valid IPv6 scopes */
            struct in6_addr *a6 = &((struct sockaddr_in6 *)&addr)->sin6_addr;

            memcpy( a6->s6_addr + 2, "\x00\x00\x00\x00\x00\x00"
                   "\x00\x00\x00\x00\x00\x02\x7f\xfe", 14 );
            if( IN6_IS_ADDR_MULTICAST( a6 ) )
                 /* force flags to zero, preserve scope */
                a6->s6_addr[1] &= 0xf;
            else
                /* Unicast IPv6 - assume global scope */
                memcpy( a6->s6_addr, "\xff\x0e", 2 );

            b_ipv6 = VLC_TRUE;
            break;
        }
#endif
304

305
        case AF_INET:
306
        {
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
            /* See RFC2365 for IPv4 scopes */
            uint32_t ipv4;

            ipv4 = ntohl( ((struct sockaddr_in *)&addr)->sin_addr.s_addr );
            /* 224.0.0.0/24 => 224.0.0.255 */
            if ((ipv4 & 0xffffff00) == 0xe0000000)
                ipv4 =  0xe00000ff;
            else
            /* 239.255.0.0/16 => 239.255.255.255 */
            if ((ipv4 & 0xffff0000) == 0xefff0000)
                ipv4 =  0xefffffff;
            else
            /* 239.192.0.0/14 => 239.195.255.255 */
            if ((ipv4 & 0xfffc0000) == 0xefc00000)
                ipv4 =  0xefc3ffff;
            else
323
324
325
            if ((ipv4 & 0xff000000) == 0xef000000)
                ipv4 = 0;
            else
326
327
            /* other addresses => 224.2.127.254 */
                ipv4 = 0xe0027ffe;
328

329
330
331
332
333
334
335
336
            if( ipv4 == 0 )
            {
                msg_Err( p_sap, "Out-of-scope multicast address "
                        "not supported by SAP: %s", p_session->psz_uri );
                vlc_mutex_unlock( &p_sap->object_lock );
                return VLC_EGENERIC;
            }

337
            ((struct sockaddr_in *)&addr)->sin_addr.s_addr = htonl( ipv4 );
338
            break;
zorglub's avatar
zorglub committed
339
        }
340

341
342
343
344
345
        default:
            vlc_mutex_unlock( &p_sap->object_lock );
            msg_Err( p_sap, "Address family %d not supported by SAP",
                     addr.ss_family );
            return VLC_EGENERIC;
zorglub's avatar
zorglub committed
346
    }
347

Rémi Denis-Courmont's avatar
Cleanup    
Rémi Denis-Courmont committed
348
    i = vlc_getnameinfo( (struct sockaddr *)&addr, addrlen,
349
350
351
                         psz_addr, sizeof( psz_addr ), NULL, NI_NUMERICHOST );

    if( i )
352
    {
353
354
355
        vlc_mutex_unlock( &p_sap->object_lock );
        msg_Err( p_sap, "%s", vlc_gai_strerror( i ) );
        return VLC_EGENERIC;
356
    }
357

358
    msg_Dbg( p_sap, "using SAP address: %s", psz_addr);
zorglub's avatar
zorglub committed
359
360
361

    /* XXX: Check for dupes */
    p_sap_session = (sap_session_t*)malloc(sizeof(sap_session_t));
362
    p_sap_session->p_address = NULL;
zorglub's avatar
zorglub committed
363
364

    /* Add the address to the buffer */
365
    for( i = 0; i < p_sap->i_addresses; i++)
zorglub's avatar
zorglub committed
366
    {
367
        if( !strcmp( psz_addr, p_sap->pp_addresses[i]->psz_address ) )
zorglub's avatar
zorglub committed
368
369
370
371
372
        {
            p_sap_session->p_address = p_sap->pp_addresses[i];
            break;
        }
    }
373
374

    if( p_sap_session->p_address == NULL )
zorglub's avatar
zorglub committed
375
376
377
378
379
380
381
382
    {
        sap_address_t *p_address = (sap_address_t *)
                                    malloc( sizeof(sap_address_t) );
        if( !p_address )
        {
            msg_Err( p_sap, "out of memory" );
            return VLC_ENOMEM;
        }
383
        p_address->psz_address = strdup( psz_addr );
384
        p_address->i_wfd = net_ConnectUDP( p_sap, psz_addr, SAP_PORT, 255 );
385
        if( p_address->i_wfd != -1 )
386
        {
387
388
            char *ptr;

389
            net_StopRecv( p_address->i_wfd );
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
390
391
            net_GetSockAddress( p_address->i_wfd, p_address->psz_machine,
                                NULL );
392
393
394
395
396

            /* removes scope if present */
            ptr = strchr( p_address->psz_machine, '%' );
            if( ptr != NULL )
                *ptr = '\0';
397
        }
zorglub's avatar
zorglub committed
398
399
400

        if( p_sap->b_control == VLC_TRUE )
        {
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
401
            p_address->i_rfd = net_ListenUDP1( (vlc_object_t*)p_sap, psz_addr, SAP_PORT );
402
403
            if( p_address->i_rfd != -1 )
                net_StopSend( p_address->i_rfd );
zorglub's avatar
zorglub committed
404
405
406
407
408
409
410
411
412
413
414
            p_address->i_buff = 0;
            p_address->b_enabled = VLC_TRUE;
            p_address->b_ready = VLC_FALSE;
            p_address->i_limit = 10000; /* 10000 bps */
            p_address->t1 = 0;
        }
        else
        {
            p_address->b_enabled = VLC_TRUE;
            p_address->b_ready = VLC_TRUE;
            p_address->i_interval = config_GetInt( p_sap,"sap-interval");
415
            p_address->i_rfd = -1;
zorglub's avatar
zorglub committed
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
        }

        if( p_address->i_wfd == -1 || (p_address->i_rfd == -1
                                        && p_sap->b_control ) )
        {
            msg_Warn( p_sap, "disabling address" );
            p_address->b_enabled = VLC_FALSE;
        }

        INSERT_ELEM( p_sap->pp_addresses,
                     p_sap->i_addresses,
                     p_sap->i_addresses,
                     p_address );
        p_sap_session->p_address = p_address;
    }

432

zorglub's avatar
zorglub committed
433
    /* Build the SAP Headers */
434
    i_header_size = ( b_ipv6 ? 16 : 4 ) + 20;
zorglub's avatar
zorglub committed
435
    psz_head = (char *) malloc( i_header_size * sizeof( char ) );
436
    if( psz_head == NULL )
zorglub's avatar
zorglub committed
437
438
439
440
441
    {
        msg_Err( p_sap, "out of memory" );
        return VLC_ENOMEM;
    }

442
443
    /* SAPv1, not encrypted, not compressed */
    psz_head[0] = b_ipv6 ? 0x30 : 0x20;
444
    psz_head[1] = 0x00; /* No authentification length */
zorglub's avatar
zorglub committed
445

446
447
448
449
    i_hash = mdate();
    psz_head[2] = (i_hash & 0xFF00) >> 8; /* Msg id hash */
    psz_head[3] = (i_hash & 0xFF);        /* Msg id hash 2 */

Eric Petit's avatar
Eric Petit committed
450
451
452
453
454
455
456
457
#if defined (HAVE_INET_PTON) || defined (WIN32)
    if( b_ipv6 )
    {
        inet_pton( AF_INET6, /* can't fail */
                   p_sap_session->p_address->psz_machine,
                   psz_head + 4 );
    }
    else
458
#endif
Eric Petit's avatar
Eric Petit committed
459
460
461
462
463
    {
        inet_pton( AF_INET, /* can't fail */
                   p_sap_session->p_address->psz_machine,
                   psz_head + 4 );
    }
464
465

    memcpy( psz_head + (b_ipv6 ? 20 : 8), "application/sdp", 15 );
zorglub's avatar
zorglub committed
466

467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
    /* If needed, build the SDP */
    if( p_session->psz_sdp == NULL )
    {
        p_session->psz_sdp = SDPGenerate( p_sap, p_session,
                                          p_sap_session->p_address );
        if( p_session->psz_sdp == NULL )
        {
            vlc_mutex_unlock( &p_sap->object_lock );
            return VLC_ENOMEM;
        }
    }

    p_sap_session->psz_sdp = strdup( p_session->psz_sdp );
    p_sap_session->i_last = 0;

zorglub's avatar
zorglub committed
482
483
484
    psz_head[ i_header_size-1 ] = '\0';
    p_sap_session->i_length = i_header_size + strlen( p_sap_session->psz_sdp);

Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
485
486
    p_sap_session->psz_data = (uint8_t *)malloc( sizeof(char)*
                                                 p_sap_session->i_length );
zorglub's avatar
zorglub committed
487
488
489
490
491
492

    /* Build the final message */
    memcpy( p_sap_session->psz_data, psz_head, i_header_size );
    memcpy( p_sap_session->psz_data+i_header_size, p_sap_session->psz_sdp,
            strlen( p_sap_session->psz_sdp) );

zorglub's avatar
zorglub committed
493
494
    free( psz_head );

zorglub's avatar
zorglub committed
495
496
497
498
499
    /* Enqueue the announce */
    INSERT_ELEM( p_sap->pp_sessions,
                 p_sap->i_sessions,
                 p_sap->i_sessions,
                 p_sap_session );
500
    msg_Dbg( p_sap,"%i addresses, %i sessions",
zorglub's avatar
zorglub committed
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
                   p_sap->i_addresses,p_sap->i_sessions);

    /* Remember the SAP session for later deletion */
    p_session->p_sap = p_sap_session;

    vlc_mutex_unlock( &p_sap->object_lock );

    return VLC_SUCCESS;
}

/* Remove a SAP Announce */
static int announce_SAPAnnounceDel( sap_handler_t *p_sap,
                             session_descriptor_t *p_session )
{
    int i;
    vlc_mutex_lock( &p_sap->object_lock );

    msg_Dbg( p_sap,"removing SAP announce %p",p_session->p_sap);

    /* Dequeue the announce */
    for( i = 0; i< p_sap->i_sessions; i++)
    {
        if( p_session->p_sap == p_sap->pp_sessions[i] )
        {
            REMOVE_ELEM( p_sap->pp_sessions,
                         p_sap->i_sessions,
                         i );
528

zorglub's avatar
zorglub committed
529
530
            FREENULL( p_session->p_sap->psz_sdp );
            FREENULL( p_session->p_sap->psz_data );
531
            free( p_session->p_sap );
zorglub's avatar
zorglub committed
532
533
534
535
            break;
        }
    }

536
537
    /* XXX: Dequeue the address too if it is not used anymore
     * TODO: - address refcount
zorglub's avatar
zorglub committed
538
             - send a SAP deletion packet */
zorglub's avatar
zorglub committed
539

540
    msg_Dbg( p_sap,"%i announcements remaining", p_sap->i_sessions );
zorglub's avatar
zorglub committed
541
542
543
544
545
546
547
548
549

    vlc_mutex_unlock( &p_sap->object_lock );

    return VLC_SUCCESS;
}

static int announce_SendSAPAnnounce( sap_handler_t *p_sap,
                                     sap_session_t *p_session )
{
550
    int i_ret;
zorglub's avatar
zorglub committed
551
552
553
554
555
556
557
558
559
560
561
562

    /* This announce has never been sent yet */
    if( p_session->i_last == 0 )
    {
        p_session->i_next = mdate()+ p_session->p_address->i_interval*1000000;
        p_session->i_last = 1;
        return VLC_SUCCESS;
    }

    if( p_session->i_next < mdate() )
    {
#ifdef EXTRA_DEBUG
563
        msg_Dbg( p_sap, "sending announce");
zorglub's avatar
zorglub committed
564
#endif
565
        i_ret = net_Write( p_sap, p_session->p_address->i_wfd, NULL,
zorglub's avatar
zorglub committed
566
567
                           p_session->psz_data,
                           p_session->i_length );
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
568
        if( i_ret != (int)p_session->i_length )
zorglub's avatar
zorglub committed
569
570
        {
            msg_Warn( p_sap, "SAP send failed on address %s (%i %i)",
571
572
                      p_session->p_address->psz_address,
                      i_ret, p_session->i_length );
zorglub's avatar
zorglub committed
573
574
575
576
577
578
579
580
581
582
583
584
        }
        p_session->i_last = p_session->i_next;
        p_session->i_next = p_session->i_last
                            + p_session->p_address->i_interval*1000000;
    }
    else
    {
        return VLC_SUCCESS;
    }
    return VLC_SUCCESS;
}

585
586
587
static char *SDPGenerate( sap_handler_t *p_sap,
                          const session_descriptor_t *p_session,
                          const sap_address_t *p_addr )
zorglub's avatar
zorglub committed
588
{
589
590
    int64_t i_sdp_id = mdate();
    int     i_sdp_version = 1 + p_sap->i_sessions + (rand()&0xfff);
591
592
    char *psz_group, *psz_name, psz_uribuf[NI_MAXNUMERICHOST], *psz_uri,
         *psz_sdp;
593
    char ipv;
594

595
596
    psz_group = p_session->psz_group;
    psz_name = p_session->psz_name;
597

598
599
    /* FIXME: really check that psz_uri is a real IP address
     * FIXME: make a common function to obtain a canonical IP address */
600
    ipv = ( strchr( p_session->psz_uri, ':' )  != NULL) ? '6' : '4';
601
602
603
604
    if( *p_session->psz_uri == '[' )
    {
        char *ptr;

Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
605
        strlcpy( psz_uribuf, p_session->psz_uri + 1, sizeof( psz_uribuf ) );
606
        ptr = strchr( psz_uribuf, '%' );
607
608
        if( ptr != NULL)
            *ptr = '\0';
609
        ptr = strchr( psz_uribuf, ']' );
610
611
        if( ptr != NULL)
            *ptr = '\0';
612
        psz_uri = psz_uribuf;
613
    }
614
615
    else
        psz_uri = p_session->psz_uri;
616

617
    /* see the lists in modules/stream_out/rtp.c for compliance stuff */
618
    if( asprintf( &psz_sdp,
619
                            "v=0\r\n"
620
                            "o=- "I64Fd" %d IN IP%c %s\r\n"
621
                            "s=%s\r\n"
622
                            "c=IN IP%c %s/%d\r\n"
623
                            "t=0 0\r\n"
624
625
626
627
                            "a=tool:"PACKAGE_STRING"\r\n"
                            "a=recvonly\r\n"
                            "a=type:broadcast\n"
                            "a=source-filter: incl IN IP%c * %s\r\n"
628
                            "m=video %d %s %d\r\n"
629
                            "%s%s%s",
630
                            i_sdp_id, i_sdp_version,
631
                            ipv, p_addr->psz_machine,
632
                            psz_name, ipv, psz_uri,
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
633
                            (p_session->i_ttl != -1) ? p_session->i_ttl : 255,
634
                            ipv, p_addr->psz_machine,
635
636
637
                            p_session->i_port, 
                            p_session->b_rtp ? "RTP/AVP" : "udp",
                            p_session->i_payload,
638
                            psz_group ? "a=x-plgroup:" : "",
639
                            psz_group ? psz_group : "", psz_group ? "\r\n" : "" ) == -1 )
640
        return NULL;
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
641

642
643
644
    msg_Dbg( p_sap, "Generated SDP (%i bytes):\n%s", strlen(psz_sdp),
             psz_sdp );
    return psz_sdp;
zorglub's avatar
zorglub committed
645
646
647
648
649
}

static int CalculateRate( sap_handler_t *p_sap, sap_address_t *p_address )
{
    int i_read;
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
650
    uint8_t buffer[SAP_MAX_BUFFER];
zorglub's avatar
zorglub committed
651
652
653
654
655
656
657
658
659
660
661
662
    int i_tot = 0;
    mtime_t i_temp;
    int i_rate;

    if( p_address->t1 == 0 )
    {
        p_address->t1 = mdate();
        return VLC_SUCCESS;
    }
    do
    {
        /* Might be too slow if we have huge data */
663
        i_read = net_ReadNonBlock( p_sap, p_address->i_rfd, NULL, buffer,
zorglub's avatar
zorglub committed
664
665
                                   SAP_MAX_BUFFER, 0 );
        i_tot += i_read;
666
    } while( i_read > 0 );
zorglub's avatar
zorglub committed
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690

    i_temp = mdate();

    /* We calculate the rate every 5 seconds */
    if( i_temp - p_address->t1 < 5000000 )
    {
        p_address->i_buff += i_tot;
        return VLC_SUCCESS;
    }

    /* Bits/second */
    i_rate = (int)(8*1000000*((mtime_t)p_address->i_buff + (mtime_t)i_tot ) /
                        (i_temp - p_address->t1 ));

    p_address->i_limit = 10000;

    p_address->i_interval = ((1000*i_rate / p_address->i_limit) *
                            (MAX_INTERVAL - MIN_INTERVAL))/1000 + MIN_INTERVAL;

    if( p_address->i_interval > MAX_INTERVAL || p_address->i_interval < 0 )
    {
        p_address->i_interval = MAX_INTERVAL;
    }
#ifdef EXTRA_DEBUG
691
    msg_Dbg( p_sap,"%s:%i: rate=%i, interval = %i s",
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
692
             p_address->psz_address,SAP_PORT, i_rate, p_address->i_interval );
zorglub's avatar
zorglub committed
693
694
695
696
697
698
699
700
701
#endif

    p_address->b_ready = VLC_TRUE;

    p_address->t1 = i_temp;
    p_address->i_buff = 0;

    return VLC_SUCCESS;
}