]> git.sesse.net Git - vlc/blob - src/stream_output/sap.c
* src/stream_output/sap.c: Fixed small memory leak.
[vlc] / src / stream_output / sap.c
1 /*****************************************************************************
2  * sap.c : SAP announce handler
3  *****************************************************************************
4  * Copyright (C) 2002-2005 the VideoLAN team
5  * $Id$
6  *
7  * Authors: Clément Stenac <zorglub@videolan.org>
8  *          Rémi Denis-Courmont <rem # videolan.org>
9  *
10  * This program is free software; you can redistribute it and/or modify
11  * it under the terms of the GNU General Public License as published by
12  * the Free Software Foundation; either version 2 of the License, or
13  * (at your option) any later version.
14  *
15  * This program is distributed in the hope that it will be useful,
16  * but WITHOUT ANY WARRANTY; without even the implied warranty of
17  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
18  * GNU General Public License for more details.
19  *
20  * You should have received a copy of the GNU General Public License
21  * along with this program; if not, write to the Free Software
22  * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111, USA.
23  *****************************************************************************/
24
25 /*****************************************************************************
26  * Preamble
27  *****************************************************************************/
28 #include <stdlib.h>                                                /* free() */
29 #include <stdio.h>                                              /* sprintf() */
30 #include <string.h>                                            /* strerror() */
31 #include <ctype.h>                                  /* tolower(), isxdigit() */
32
33 #include <vlc/vlc.h>
34 #include <vlc/sout.h>
35
36 #include "network.h"
37 #if defined( WIN32 ) || defined( UNDER_CE )
38 #   if defined(UNDER_CE) && defined(sockaddr_storage)
39 #       undef sockaddr_storage
40 #   endif
41 #   include <winsock2.h>
42 #   include <ws2tcpip.h>
43 #else
44 #   include <netdb.h>
45 #   include <arpa/inet.h>
46 #endif
47 #include "charset.h"
48
49 /* SAP is always on that port */
50 #define SAP_PORT 9875
51
52 #define DEFAULT_PORT "1234"
53
54 #undef EXTRA_DEBUG
55
56 /* SAP Specific structures */
57
58 /* 100ms */
59 #define SAP_IDLE ((mtime_t)(0.100*CLOCK_FREQ))
60 #define SAP_MAX_BUFFER 65534
61 #define MIN_INTERVAL 2
62 #define MAX_INTERVAL 300
63
64 /* A SAP announce address. For each of these, we run the
65  * control flow algorithm */
66 struct sap_address_t
67 {
68     char *psz_address;
69     char psz_machine[NI_MAXNUMERICHOST];
70     int i_port;
71     int i_rfd; /* Read socket */
72     int i_wfd; /* Write socket */
73
74     /* Used for flow control */
75     mtime_t t1;
76     vlc_bool_t b_enabled;
77     vlc_bool_t b_ready;
78     int i_interval;
79     int i_buff;
80     int i_limit;
81 };
82
83 /*****************************************************************************
84  * Local prototypes
85  *****************************************************************************/
86 static void RunThread( vlc_object_t *p_this);
87 static int CalculateRate( sap_handler_t *p_sap, sap_address_t *p_address );
88 static char *SDPGenerate( sap_handler_t *p_sap,
89                           const session_descriptor_t *p_session,
90                           const sap_address_t *p_addr );
91
92 static int announce_SendSAPAnnounce( sap_handler_t *p_sap,
93                                      sap_session_t *p_session );
94
95
96 static int announce_SAPAnnounceAdd( sap_handler_t *p_sap,
97                              session_descriptor_t *p_session );
98
99 static int announce_SAPAnnounceDel( sap_handler_t *p_sap,
100                              session_descriptor_t *p_session );
101 static char *convert_to_utf8( struct sap_handler_t *p_this, char *psz_local );
102
103 #define FREE( p ) if( p ) { free( p ); (p) = NULL; }
104
105
106 /**
107  * Create the SAP handler
108  *
109  * \param p_announce the parent announce_handler
110  * \return the newly created SAP handler or NULL on error
111  */
112 sap_handler_t *announce_SAPHandlerCreate( announce_handler_t *p_announce )
113 {
114     sap_handler_t *p_sap;
115     char *psz_charset;
116
117     p_sap = vlc_object_create( p_announce, sizeof( sap_handler_t ) );
118
119     if( !p_sap )
120     {
121         msg_Err( p_announce, "out of memory" );
122         return NULL;
123     }
124
125     vlc_mutex_init( p_sap, &p_sap->object_lock );
126
127     vlc_current_charset( &psz_charset );
128     p_sap->iconvHandle = vlc_iconv_open( "UTF-8", psz_charset );
129     free( psz_charset );
130     if( p_sap->iconvHandle == (vlc_iconv_t)(-1) )
131         msg_Warn( p_sap, "Unable to do requested conversion" );
132
133     p_sap->pf_add = announce_SAPAnnounceAdd;
134     p_sap->pf_del = announce_SAPAnnounceDel;
135
136     p_sap->i_sessions = 0;
137     p_sap->i_addresses = 0;
138     p_sap->i_current_session = 0;
139
140     p_sap->b_control = config_GetInt( p_sap, "sap-flow-control");
141
142     if( vlc_thread_create( p_sap, "sap handler", RunThread,
143                        VLC_THREAD_PRIORITY_LOW, VLC_FALSE ) )
144     {
145         msg_Dbg( p_announce, "Unable to spawn SAP handler thread");
146         free( p_sap );
147         return NULL;
148     };
149     msg_Dbg( p_announce, "thread created, %i sessions", p_sap->i_sessions);
150     return p_sap;
151 }
152
153 /**
154  *  Destroy the SAP handler
155  *  \param p_this the SAP Handler to destroy
156  *  \return nothing
157  */
158 void announce_SAPHandlerDestroy( sap_handler_t *p_sap )
159 {
160     int i;
161
162     vlc_mutex_destroy( &p_sap->object_lock );
163
164     /* Free the remaining sessions */
165     for( i = 0 ; i< p_sap->i_sessions ; i++)
166     {
167         sap_session_t *p_session = p_sap->pp_sessions[i];
168         FREE( p_session->psz_sdp );
169         FREE( p_session->psz_data );
170         REMOVE_ELEM( p_sap->pp_sessions, p_sap->i_sessions , i );
171         FREE( p_session );
172     }
173
174     /* Free the remaining addresses */
175     for( i = 0 ; i< p_sap->i_addresses ; i++)
176     {
177         sap_address_t *p_address = p_sap->pp_addresses[i];
178         FREE( p_address->psz_address );
179         if( p_address->i_rfd > -1 )
180         {
181             net_Close( p_address->i_rfd );
182         }
183         if( p_address->i_wfd > -1 && p_sap->b_control )
184         {
185             net_Close( p_address->i_wfd );
186         }
187         REMOVE_ELEM( p_sap->pp_addresses, p_sap->i_addresses, i );
188         FREE( p_address );
189     }
190
191     if( p_sap->iconvHandle != (vlc_iconv_t)(-1) )
192         vlc_iconv_close( p_sap->iconvHandle );
193
194     /* Free the structure */
195     vlc_object_destroy( p_sap );
196 }
197
198 /**
199  * main SAP handler thread
200  * \param p_this the SAP Handler object
201  * \return nothing
202  */
203 static void RunThread( vlc_object_t *p_this)
204 {
205     sap_handler_t *p_sap = (sap_handler_t*)p_this;
206     sap_session_t *p_session;
207
208     while( !p_sap->b_die )
209     {
210         int i;
211
212         /* If needed, get the rate info */
213         if( p_sap->b_control == VLC_TRUE )
214         {
215             for( i = 0 ; i< p_sap->i_addresses ; i++)
216             {
217                 if( p_sap->pp_addresses[i]->b_enabled == VLC_TRUE )
218                 {
219                     CalculateRate( p_sap, p_sap->pp_addresses[i] );
220                 }
221             }
222         }
223
224         /* Find the session to announce */
225         vlc_mutex_lock( &p_sap->object_lock );
226         if( p_sap->i_sessions > p_sap->i_current_session + 1)
227         {
228             p_sap->i_current_session++;
229         }
230         else if( p_sap->i_sessions > 0)
231         {
232             p_sap->i_current_session = 0;
233         }
234         else
235         {
236             vlc_mutex_unlock( &p_sap->object_lock );
237             msleep( SAP_IDLE );
238             continue;
239         }
240         p_session = p_sap->pp_sessions[p_sap->i_current_session];
241         vlc_mutex_unlock( &p_sap->object_lock );
242
243         /* And announce it */
244         if( p_session->p_address->b_enabled == VLC_TRUE &&
245             p_session->p_address->b_ready == VLC_TRUE )
246         {
247             announce_SendSAPAnnounce( p_sap, p_session );
248         }
249
250         msleep( SAP_IDLE );
251     }
252 }
253
254 /* Add a SAP announce */
255 static int announce_SAPAnnounceAdd( sap_handler_t *p_sap,
256                              session_descriptor_t *p_session )
257 {
258     int i_header_size, i;
259     char *psz_head, psz_addr[NI_MAXNUMERICHOST];
260     vlc_bool_t b_ipv6 = VLC_FALSE;
261     sap_session_t *p_sap_session;
262     mtime_t i_hash;
263     struct addrinfo hints, *res;
264     struct sockaddr_storage addr;
265
266     vlc_mutex_lock( &p_sap->object_lock );
267
268     if( p_session->psz_uri == NULL )
269     {
270         vlc_mutex_unlock( &p_sap->object_lock );
271         msg_Err( p_sap, "*FIXME* Unexpected NULL URI for SAP announce" );
272         msg_Err( p_sap, "This should not happen. VLC needs fixing." );
273         return VLC_EGENERIC;
274     }
275
276     /* Determine SAP multicast address automatically */
277     memset( &hints, 0, sizeof( hints ) );
278     hints.ai_socktype = SOCK_DGRAM;
279     hints.ai_flags = AI_NUMERICHOST;
280
281     i = vlc_getaddrinfo( (vlc_object_t *)p_sap, p_session->psz_uri, 0,
282                          &hints, &res );
283     if( i )
284     {
285         vlc_mutex_unlock( &p_sap->object_lock );
286         msg_Err( p_sap, "Invalid URI for SAP announce: %s: %s",
287                  p_session->psz_uri, vlc_gai_strerror( i ) );
288         return VLC_EGENERIC;
289     }
290
291     if( (unsigned)res->ai_addrlen > sizeof( addr ) )
292     {
293         vlc_mutex_unlock( &p_sap->object_lock );
294         vlc_freeaddrinfo( res );
295         msg_Err( p_sap, "Unsupported address family of size %d > %u",
296                  res->ai_addrlen, sizeof( addr ) );
297         return VLC_EGENERIC;
298     }
299
300     memcpy( &addr, res->ai_addr, res->ai_addrlen );
301
302     switch( addr.ss_family )
303     {
304 #if defined (HAVE_INET_PTON) || defined (WIN32)
305         case AF_INET6:
306         {
307             /* See RFC3513 for list of valid IPv6 scopes */
308             struct in6_addr *a6 = &((struct sockaddr_in6 *)&addr)->sin6_addr;
309
310             memcpy( a6->s6_addr + 2, "\x00\x00\x00\x00\x00\x00"
311                    "\x00\x00\x00\x00\x00\x02\x7f\xfe", 14 );
312             if( IN6_IS_ADDR_MULTICAST( a6 ) )
313                  /* force flags to zero, preserve scope */
314                 a6->s6_addr[1] &= 0xf;
315             else
316                 /* Unicast IPv6 - assume global scope */
317                 memcpy( a6->s6_addr, "\xff\x0e", 2 );
318
319             b_ipv6 = VLC_TRUE;
320             break;
321         }
322 #endif
323
324         case AF_INET:
325         {
326             /* See RFC2365 for IPv4 scopes */
327             uint32_t ipv4;
328
329             ipv4 = ntohl( ((struct sockaddr_in *)&addr)->sin_addr.s_addr );
330             /* 224.0.0.0/24 => 224.0.0.255 */
331             if ((ipv4 & 0xffffff00) == 0xe0000000)
332                 ipv4 =  0xe00000ff;
333             else
334             /* 239.255.0.0/16 => 239.255.255.255 */
335             if ((ipv4 & 0xffff0000) == 0xefff0000)
336                 ipv4 =  0xefffffff;
337             else
338             /* 239.192.0.0/14 => 239.195.255.255 */
339             if ((ipv4 & 0xfffc0000) == 0xefc00000)
340                 ipv4 =  0xefc3ffff;
341             else
342             /* other addresses => 224.2.127.254 */
343                 ipv4 = 0xe0027ffe;
344             break;
345         }
346         
347         default:
348             vlc_mutex_unlock( &p_sap->object_lock );
349             vlc_freeaddrinfo( res );
350             msg_Err( p_sap, "Address family %d not supported by SAP",
351                      addr.ss_family );
352             return VLC_EGENERIC;
353     }
354
355     i = vlc_getnameinfo( (struct sockaddr *)&addr, res->ai_addrlen,
356                          psz_addr, sizeof( psz_addr ), NULL, NI_NUMERICHOST );
357     vlc_freeaddrinfo( res );
358
359     if( i )
360     {
361         vlc_mutex_unlock( &p_sap->object_lock );
362         msg_Err( p_sap, "%s", vlc_gai_strerror( i ) );
363         return VLC_EGENERIC;
364     }
365
366     msg_Dbg( p_sap, "using SAP address: %s", psz_addr);
367
368     /* XXX: Check for dupes */
369     p_sap_session = (sap_session_t*)malloc(sizeof(sap_session_t));
370     p_sap_session->p_address = NULL;
371
372     /* Add the address to the buffer */
373     for( i = 0; i < p_sap->i_addresses; i++)
374     {
375         if( !strcmp( psz_addr, p_sap->pp_addresses[i]->psz_address ) )
376         {
377             p_sap_session->p_address = p_sap->pp_addresses[i];
378             break;
379         }
380     }
381
382     if( p_sap_session->p_address == NULL )
383     {
384         sap_address_t *p_address = (sap_address_t *)
385                                     malloc( sizeof(sap_address_t) );
386         if( !p_address )
387         {
388             msg_Err( p_sap, "out of memory" );
389             return VLC_ENOMEM;
390         }
391         p_address->psz_address = strdup( psz_addr );
392         p_address->i_port  =  9875;
393         p_address->i_wfd = net_OpenUDP( p_sap, "", 0, psz_addr,
394                                         p_address->i_port );
395         if( p_address->i_wfd != -1 )
396         {
397             char *ptr;
398
399             net_StopRecv( p_address->i_wfd );
400             net_GetSockAddress( p_address->i_wfd, p_address->psz_machine,
401                                 NULL );
402
403             /* removes scope if present */
404             ptr = strchr( p_address->psz_machine, '%' );
405             if( ptr != NULL )
406                 *ptr = '\0';
407         }
408
409         if( p_sap->b_control == VLC_TRUE )
410         {
411             p_address->i_rfd = net_OpenUDP( p_sap, psz_addr,
412                                             p_address->i_port, "", 0 );
413             if( p_address->i_rfd != -1 )
414                 net_StopSend( p_address->i_rfd );
415             p_address->i_buff = 0;
416             p_address->b_enabled = VLC_TRUE;
417             p_address->b_ready = VLC_FALSE;
418             p_address->i_limit = 10000; /* 10000 bps */
419             p_address->t1 = 0;
420         }
421         else
422         {
423             p_address->b_enabled = VLC_TRUE;
424             p_address->b_ready = VLC_TRUE;
425             p_address->i_interval = config_GetInt( p_sap,"sap-interval");
426             p_address->i_rfd = -1;
427         }
428
429         if( p_address->i_wfd == -1 || (p_address->i_rfd == -1
430                                         && p_sap->b_control ) )
431         {
432             msg_Warn( p_sap, "disabling address" );
433             p_address->b_enabled = VLC_FALSE;
434         }
435
436         INSERT_ELEM( p_sap->pp_addresses,
437                      p_sap->i_addresses,
438                      p_sap->i_addresses,
439                      p_address );
440         p_sap_session->p_address = p_address;
441     }
442
443
444     /* Build the SAP Headers */
445     i_header_size = ( b_ipv6 ? 16 : 4 ) + 20;
446     psz_head = (char *) malloc( i_header_size * sizeof( char ) );
447     if( psz_head == NULL )
448     {
449         msg_Err( p_sap, "out of memory" );
450         return VLC_ENOMEM;
451     }
452
453     /* SAPv1, not encrypted, not compressed */
454     psz_head[0] = b_ipv6 ? 0x30 : 0x20;
455     psz_head[1] = 0x00; /* No authentification length */
456
457     i_hash = mdate();
458     psz_head[2] = (i_hash & 0xFF00) >> 8; /* Msg id hash */
459     psz_head[3] = (i_hash & 0xFF);        /* Msg id hash 2 */
460
461     inet_pton( b_ipv6 ? AF_INET6 : AF_INET, /* can't fail */
462                p_sap_session->p_address->psz_machine, psz_head + 4 );
463
464     memcpy( psz_head + (b_ipv6 ? 20 : 8), "application/sdp", 15 );
465
466     /* If needed, build the SDP */
467     if( p_session->psz_sdp == NULL )
468     {
469         p_session->psz_sdp = SDPGenerate( p_sap, p_session,
470                                           p_sap_session->p_address );
471         if( p_session->psz_sdp == NULL )
472         {
473             vlc_mutex_unlock( &p_sap->object_lock );
474             return VLC_ENOMEM;
475         }
476     }
477
478     p_sap_session->psz_sdp = strdup( p_session->psz_sdp );
479     p_sap_session->i_last = 0;
480
481     psz_head[ i_header_size-1 ] = '\0';
482     p_sap_session->i_length = i_header_size + strlen( p_sap_session->psz_sdp);
483
484     p_sap_session->psz_data = (uint8_t *)malloc( sizeof(char)*
485                                                  p_sap_session->i_length );
486
487     /* Build the final message */
488     memcpy( p_sap_session->psz_data, psz_head, i_header_size );
489     memcpy( p_sap_session->psz_data+i_header_size, p_sap_session->psz_sdp,
490             strlen( p_sap_session->psz_sdp) );
491
492     free( psz_head );
493
494     /* Enqueue the announce */
495     INSERT_ELEM( p_sap->pp_sessions,
496                  p_sap->i_sessions,
497                  p_sap->i_sessions,
498                  p_sap_session );
499     msg_Dbg( p_sap,"Addresses: %i  Sessions: %i",
500                    p_sap->i_addresses,p_sap->i_sessions);
501
502     /* Remember the SAP session for later deletion */
503     p_session->p_sap = p_sap_session;
504
505     vlc_mutex_unlock( &p_sap->object_lock );
506
507     return VLC_SUCCESS;
508 }
509
510 /* Remove a SAP Announce */
511 static int announce_SAPAnnounceDel( sap_handler_t *p_sap,
512                              session_descriptor_t *p_session )
513 {
514     int i;
515     vlc_mutex_lock( &p_sap->object_lock );
516
517     msg_Dbg( p_sap,"removing SAP announce %p",p_session->p_sap);
518
519     /* Dequeue the announce */
520     for( i = 0; i< p_sap->i_sessions; i++)
521     {
522         if( p_session->p_sap == p_sap->pp_sessions[i] )
523         {
524             REMOVE_ELEM( p_sap->pp_sessions,
525                          p_sap->i_sessions,
526                          i );
527
528             FREE( p_session->p_sap->psz_sdp );
529             FREE( p_session->p_sap->psz_data );
530             free( p_session->p_sap );
531             break;
532         }
533     }
534
535     /* XXX: Dequeue the address too if it is not used anymore
536      * TODO: - address refcount
537              - send a SAP deletion packet */
538
539     msg_Dbg( p_sap,"%i announces remaining", p_sap->i_sessions );
540
541     vlc_mutex_unlock( &p_sap->object_lock );
542
543     return VLC_SUCCESS;
544 }
545
546 static int announce_SendSAPAnnounce( sap_handler_t *p_sap,
547                                      sap_session_t *p_session )
548 {
549     int i_ret;
550
551     /* This announce has never been sent yet */
552     if( p_session->i_last == 0 )
553     {
554         p_session->i_next = mdate()+ p_session->p_address->i_interval*1000000;
555         p_session->i_last = 1;
556         return VLC_SUCCESS;
557     }
558
559     if( p_session->i_next < mdate() )
560     {
561 #ifdef EXTRA_DEBUG
562         msg_Dbg( p_sap, "Sending announce");
563 #endif
564         i_ret = net_Write( p_sap, p_session->p_address->i_wfd, NULL,
565                            p_session->psz_data,
566                            p_session->i_length );
567         if( i_ret != (int)p_session->i_length )
568         {
569             msg_Warn( p_sap, "SAP send failed on address %s (%i %i)",
570                    p_session->p_address->psz_address,
571                    i_ret, p_session->i_length );
572         }
573         p_session->i_last = p_session->i_next;
574         p_session->i_next = p_session->i_last
575                             + p_session->p_address->i_interval*1000000;
576     }
577     else
578     {
579         return VLC_SUCCESS;
580     }
581     return VLC_SUCCESS;
582 }
583
584 static char *SDPGenerate( sap_handler_t *p_sap,
585                           const session_descriptor_t *p_session,
586                           const sap_address_t *p_addr )
587 {
588     int64_t i_sdp_id = mdate();
589     int     i_sdp_version = 1 + p_sap->i_sessions + (rand()&0xfff);
590     char *psz_group, *psz_name, psz_uribuf[NI_MAXNUMERICHOST], *psz_uri,
591          *psz_sdp;
592     char ipv;
593
594     psz_group = convert_to_utf8( p_sap, p_session->psz_group );
595     psz_name = convert_to_utf8( p_sap, p_session->psz_name );
596     if( psz_name == NULL )
597     {
598         FREE( psz_group );
599         return NULL;
600     }
601
602     /* FIXME: really check that psz_uri is a real IP address
603      * FIXME: make a common function to obtain a canonical IP address */
604     ipv = ( strchr( p_session->psz_uri, ':' )  != NULL) ? '6' : '4';
605     if( *p_session->psz_uri == '[' )
606     {
607         char *ptr;
608
609         strncpy( psz_uribuf, p_session->psz_uri + 1, sizeof( psz_uribuf ) );
610         psz_uribuf[sizeof( psz_uribuf ) - 1] = '\0';
611         ptr = strchr( psz_uribuf, '%' );
612         if( ptr != NULL)
613             *ptr = '\0';
614         ptr = strchr( psz_uribuf, ']' );
615         if( ptr != NULL)
616             *ptr = '\0';
617         psz_uri = psz_uribuf;
618     }
619     else
620         psz_uri = p_session->psz_uri;
621
622     /* see the lists in modules/stream_out/rtp.c for compliance stuff */
623     if( asprintf( &psz_sdp,
624                             "v=0\r\n"
625                             "o=- "I64Fd" %d IN IP%c %s\r\n"
626                             "s=%s\r\n"
627                             "t=0 0\r\n"
628                             "c=IN IP%c %s/%d\r\n"
629                             "m=video %d udp %d\r\n"
630                             "a=tool:"PACKAGE_STRING"\r\n"
631                             "a=type:broadcast\r\n",
632                             i_sdp_id, i_sdp_version,
633                             ipv, p_addr->psz_machine,
634                             psz_name, ipv,
635                             psz_uri, p_session->i_ttl,
636                             p_session->i_port, p_session->i_payload ) == -1 )
637     {
638         free( psz_name );
639         FREE( psz_group );
640         return NULL;
641     }
642     
643     free( psz_name );
644
645     if( psz_group )
646     {
647         /* FIXME: this is illegal use of sprintf */
648         sprintf( psz_sdp, "%sa=x-plgroup:%s\r\n", psz_sdp, psz_group );
649         free( psz_group );
650     }
651
652     msg_Dbg( p_sap, "Generated SDP (%i bytes):\n%s", strlen(psz_sdp),
653              psz_sdp );
654     return psz_sdp;
655 }
656
657 static int CalculateRate( sap_handler_t *p_sap, sap_address_t *p_address )
658 {
659     int i_read;
660     uint8_t buffer[SAP_MAX_BUFFER];
661     int i_tot = 0;
662     mtime_t i_temp;
663     int i_rate;
664
665     if( p_address->t1 == 0 )
666     {
667         p_address->t1 = mdate();
668         return VLC_SUCCESS;
669     }
670     do
671     {
672         /* Might be too slow if we have huge data */
673         i_read = net_ReadNonBlock( p_sap, p_address->i_rfd, NULL, buffer,
674                                    SAP_MAX_BUFFER, 0 );
675         i_tot += i_read;
676     } while( i_read > 0 && i_tot < SAP_MAX_BUFFER );
677
678     i_temp = mdate();
679
680     /* We calculate the rate every 5 seconds */
681     if( i_temp - p_address->t1 < 5000000 )
682     {
683         p_address->i_buff += i_tot;
684         return VLC_SUCCESS;
685     }
686
687     /* Bits/second */
688     i_rate = (int)(8*1000000*((mtime_t)p_address->i_buff + (mtime_t)i_tot ) /
689                         (i_temp - p_address->t1 ));
690
691     p_address->i_limit = 10000;
692
693     p_address->i_interval = ((1000*i_rate / p_address->i_limit) *
694                             (MAX_INTERVAL - MIN_INTERVAL))/1000 + MIN_INTERVAL;
695
696     if( p_address->i_interval > MAX_INTERVAL || p_address->i_interval < 0 )
697     {
698         p_address->i_interval = MAX_INTERVAL;
699     }
700 #ifdef EXTRA_DEBUG
701     msg_Dbg( p_sap,"%s:%i : Rate=%i, Interval = %i s",
702                     p_address->psz_address,p_address->i_port,
703                     i_rate,
704                     p_address->i_interval );
705 #endif
706
707     p_address->b_ready = VLC_TRUE;
708
709     p_address->t1 = i_temp;
710     p_address->i_buff = 0;
711
712     return VLC_SUCCESS;
713 }
714
715
716 static char *convert_to_utf8( struct sap_handler_t *p_this, char *psz_local )
717 {
718     char *psz_unicode, *psz_in, *psz_out;
719     size_t ret, i_in, i_out;
720
721     if( psz_local == NULL )
722         return NULL;
723     if ( p_this->iconvHandle == (vlc_iconv_t)(-1) )
724         return strdup( psz_local );
725
726     psz_in = psz_local;
727     i_in = strlen( psz_local );
728
729     i_out = 6 * i_in;
730     psz_unicode = malloc( i_out + 1 );
731     if( psz_unicode == NULL )
732         return strdup( psz_local );
733     psz_out = psz_unicode;
734
735     ret = vlc_iconv( p_this->iconvHandle,
736                      &psz_in, &i_in, &psz_out, &i_out);
737     if( ret == (size_t)(-1) || i_in )
738     {
739         msg_Warn( p_this, "Failed to convert \"%s\" to UTF-8", psz_local );
740         free(psz_unicode);
741         return strdup( psz_local );
742     }
743     *psz_out = '\0';
744     return psz_unicode;
745 }