00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
00022
00023
00024
00025
00026
00027 #include <stdlib.h>
00028
00029 #include <vlc/vlc.h>
00030 #include <vlc/input.h>
00031 #include <vlc/intf.h>
00032
00033 #include <network.h>
00034 #include <charset.h>
00035
00036 #include <ctype.h>
00037 #include <errno.h>
00038
00039 #ifdef HAVE_UNISTD_H
00040 # include <unistd.h>
00041 #endif
00042 #ifdef HAVE_SYS_TIME_H
00043 # include <sys/time.h>
00044 #endif
00045
00046 #ifdef HAVE_ZLIB_H
00047 # include <zlib.h>
00048 #endif
00049
00050
00051
00052
00053
00054 #define MAX_LINE_LENGTH 256
00055
00056
00057 #define SAP_PORT 9875
00058
00059 #define SAP_V4_GLOBAL_ADDRESS "224.2.127.254"
00060
00061 #define SAP_V4_ORG_ADDRESS "239.195.255.255"
00062
00063 #define SAP_V4_LOCAL_ADDRESS "239.255.255.255"
00064
00065 #define SAP_V4_LINK_ADDRESS "224.0.0.255"
00066 #define ADD_SESSION 1
00067
00068 #define SAP_V6_1 "FF0"
00069
00070 #define SAP_V6_2 "::2:7FFE"
00071
00072
00073 static const char ipv6_scopes[] = "1456789ABCDE";
00074
00075
00076
00077
00078
00079 #define SAP_ADDR_TEXT N_( "SAP multicast address" )
00080 #define SAP_ADDR_LONGTEXT N_( "Listen for SAP announcements on another address" )
00081 #define SAP_IPV4_TEXT N_( "IPv4-SAP listening" )
00082 #define SAP_IPV4_LONGTEXT N_( \
00083 "Set this if you want the SAP module to listen to IPv4 announcements " \
00084 "on the standard address." )
00085 #define SAP_IPV6_TEXT N_( "IPv6-SAP listening" )
00086 #define SAP_IPV6_LONGTEXT N_( \
00087 "Set this if you want the SAP module to listen to IPv6 announcements " \
00088 "on the standard address." )
00089 #define SAP_SCOPE_TEXT N_( "IPv6 SAP scope" )
00090 #define SAP_SCOPE_LONGTEXT N_( \
00091 "Sets the scope for IPv6 announcements (default is 8)." )
00092 #define SAP_TIMEOUT_TEXT N_( "SAP timeout (seconds)" )
00093 #define SAP_TIMEOUT_LONGTEXT N_( \
00094 "Sets the time before SAP items get deleted if no new announcement " \
00095 "is received." )
00096 #define SAP_PARSE_TEXT N_( "Try to parse the SAP" )
00097 #define SAP_PARSE_LONGTEXT N_( \
00098 "When SAP can it will try to parse the SAP. If you don't select " \
00099 "this, all announcements will be parsed by the livedotcom module." )
00100 #define SAP_STRICT_TEXT N_( "SAP Strict mode" )
00101 #define SAP_STRICT_LONGTEXT N_( \
00102 "When this is set, the SAP parser will discard some non-compliant " \
00103 "announcements." )
00104 #define SAP_CACHE_TEXT N_("Use SAP cache")
00105 #define SAP_CACHE_LONGTEXT N_( \
00106 "If this option is selected, a SAP caching mechanism will be used. " \
00107 "This will result in lower SAP startup time, but you could end up " \
00108 "with items corresponding to legacy streams." )
00109 #define SAP_TIMESHIFT_TEXT N_("Allow timeshifting")
00110 #define SAP_TIMESHIFT_LONGTEXT N_( \
00111 "Enable timeshifting automatically for streams " \
00112 "discovered through SAP announcements." )
00113
00114
00115 static int Open ( vlc_object_t * );
00116 static void Close( vlc_object_t * );
00117 static int OpenDemux ( vlc_object_t * );
00118 static void CloseDemux ( vlc_object_t * );
00119
00120 vlc_module_begin();
00121 set_shortname( _("SAP"));
00122 set_description( _("SAP Announcements") );
00123 set_category( CAT_PLAYLIST );
00124 set_subcategory( SUBCAT_PLAYLIST_SD );
00125
00126 add_string( "sap-addr", NULL, NULL,
00127 SAP_ADDR_TEXT, SAP_ADDR_LONGTEXT, VLC_TRUE );
00128 add_bool( "sap-ipv4", 1 , NULL,
00129 SAP_IPV4_TEXT,SAP_IPV4_LONGTEXT, VLC_TRUE );
00130 add_bool( "sap-ipv6", 1 , NULL,
00131 SAP_IPV6_TEXT, SAP_IPV6_LONGTEXT, VLC_TRUE );
00132 add_integer( "sap-timeout", 1800, NULL,
00133 SAP_TIMEOUT_TEXT, SAP_TIMEOUT_LONGTEXT, VLC_TRUE );
00134 add_bool( "sap-parse", 1 , NULL,
00135 SAP_PARSE_TEXT,SAP_PARSE_LONGTEXT, VLC_TRUE );
00136 add_bool( "sap-strict", 0 , NULL,
00137 SAP_STRICT_TEXT,SAP_STRICT_LONGTEXT, VLC_TRUE );
00138 add_bool( "sap-cache", 0 , NULL,
00139 SAP_CACHE_TEXT,SAP_CACHE_LONGTEXT, VLC_TRUE );
00140 add_bool( "sap-timeshift", 0 , NULL,
00141 SAP_TIMESHIFT_TEXT,SAP_TIMESHIFT_LONGTEXT, VLC_FALSE );
00142
00143 set_capability( "services_discovery", 0 );
00144 set_callbacks( Open, Close );
00145
00146 add_submodule();
00147 set_description( _("SDP file parser for UDP") );
00148 add_shortcut( "sdp" );
00149 set_capability( "demux2", 51 );
00150 set_callbacks( OpenDemux, CloseDemux );
00151 vlc_module_end();
00152
00153
00154
00155
00156
00157
00158 typedef struct sdp_t sdp_t;
00159 typedef struct attribute_t attribute_t;
00160 typedef struct sap_announce_t sap_announce_t;
00161
00162
00163 struct sdp_t
00164 {
00165 char *psz_sdp;
00166
00167
00168 char *psz_sessionname;
00169
00170
00171 char *psz_connection;
00172 char *psz_media;
00173
00174
00175 char *psz_username;
00176 char *psz_network_type;
00177 char *psz_address_type;
00178 char *psz_address;
00179 int64_t i_session_id;
00180
00181
00182 char *psz_uri;
00183
00184 int i_in;
00185
00186 int i_media;
00187 int i_media_type;
00188
00189 int i_attributes;
00190 attribute_t **pp_attributes;
00191 };
00192
00193 struct attribute_t
00194 {
00195 char *psz_field;
00196 char *psz_value;
00197 };
00198
00199 struct sap_announce_t
00200 {
00201 mtime_t i_last;
00202
00203 uint16_t i_hash;
00204 uint32_t i_source[4];
00205
00206
00207 sdp_t *p_sdp;
00208
00209 int i_item_id;
00210
00211 };
00212
00213 struct services_discovery_sys_t
00214 {
00215
00216 int i_fd;
00217 int *pi_fd;
00218
00219
00220 playlist_item_t *p_node;
00221 playlist_t *p_playlist;
00222
00223
00224 int i_announces;
00225 struct sap_announce_t **pp_announces;
00226
00227
00228 vlc_bool_t b_strict;
00229 vlc_bool_t b_parse;
00230 vlc_bool_t b_timeshift;
00231
00232 int i_timeout;
00233 };
00234
00235 struct demux_sys_t
00236 {
00237 sdp_t *p_sdp;
00238 };
00239
00240
00241
00242
00243
00244
00245
00246 static int Demux( demux_t *p_demux );
00247 static int Control( demux_t *, int, va_list );
00248 static void Run ( services_discovery_t *p_sd );
00249
00250
00251 static int ParseConnection( vlc_object_t *p_obj, sdp_t *p_sdp );
00252 static int ParseSAP( services_discovery_t *p_sd, uint8_t *p_buffer, int i_read );
00253 static sdp_t * ParseSDP( vlc_object_t *p_sd, char* psz_sdp );
00254 static sap_announce_t *CreateAnnounce( services_discovery_t *, uint16_t, sdp_t * );
00255 static int RemoveAnnounce( services_discovery_t *p_sd, sap_announce_t *p_announce );
00256
00257
00258 static void CacheLoad( services_discovery_t *p_sd );
00259 static void CacheSave( services_discovery_t *p_sd );
00260
00261 static char *GetAttribute( sdp_t *p_sdp, const char *psz_search );
00262 static vlc_bool_t IsSameSession( sdp_t *p_sdp1, sdp_t *p_sdp2 );
00263 static int InitSocket( services_discovery_t *p_sd, char *psz_address, int i_port );
00264 #ifdef HAVE_ZLIB_H
00265 static int Decompress( unsigned char *psz_src, unsigned char **_dst, int i_len );
00266 #endif
00267 static void FreeSDP( sdp_t *p_sdp );
00268
00269
00270 #define FREE( p ) \
00271 if( p ) { free( p ); (p) = NULL; }
00272
00273
00274
00275 static int Open( vlc_object_t *p_this )
00276 {
00277 services_discovery_t *p_sd = ( services_discovery_t* )p_this;
00278 services_discovery_sys_t *p_sys = (services_discovery_sys_t *)
00279 malloc( sizeof( services_discovery_sys_t ) );
00280
00281 playlist_view_t *p_view;
00282 vlc_value_t val;
00283
00284 p_sys->i_timeout = var_CreateGetInteger( p_sd, "sap-timeout" );
00285
00286 p_sd->pf_run = Run;
00287 p_sd->p_sys = p_sys;
00288
00289 p_sys->pi_fd = NULL;
00290 p_sys->i_fd = 0;
00291
00292 p_sys->b_strict = var_CreateGetInteger( p_sd, "sap-strict");
00293 p_sys->b_parse = var_CreateGetInteger( p_sd, "sap-parse" );
00294
00295 if( var_CreateGetInteger( p_sd, "sap-cache" ) )
00296 {
00297 CacheLoad( p_sd );
00298 }
00299
00300
00301 p_sys->b_timeshift = var_CreateGetInteger( p_sd, "sap-timeshift" )
00302 ? VLC_TRUE : VLC_FALSE;
00303
00304
00305 p_sys->p_playlist = (playlist_t *)vlc_object_find( p_sd,
00306 VLC_OBJECT_PLAYLIST,
00307 FIND_ANYWHERE );
00308 if( !p_sys->p_playlist )
00309 {
00310 msg_Warn( p_sd, "unable to find playlist, cancelling SAP listening");
00311 return VLC_EGENERIC;
00312 }
00313
00314 p_view = playlist_ViewFind( p_sys->p_playlist, VIEW_CATEGORY );
00315 p_sys->p_node = playlist_NodeCreate( p_sys->p_playlist, VIEW_CATEGORY,
00316 _("Session Announcements (SAP)"), p_view->p_root );
00317 p_sys->p_node->i_flags |= PLAYLIST_RO_FLAG;
00318 p_sys->p_node->i_flags &= ~PLAYLIST_SKIP_FLAG;
00319 val.b_bool = VLC_TRUE;
00320 var_Set( p_sys->p_playlist, "intf-change", val );
00321
00322 p_sys->i_announces = 0;
00323 p_sys->pp_announces = NULL;
00324
00325 return VLC_SUCCESS;
00326 }
00327
00328
00329
00330
00331 static int OpenDemux( vlc_object_t *p_this )
00332 {
00333 demux_t *p_demux = (demux_t *)p_this;
00334 uint8_t *p_peek;
00335 int i_max_sdp = 1024;
00336 int i_sdp = 0;
00337 char *psz_sdp = NULL;
00338 sdp_t *p_sdp = NULL;
00339
00340 if( !var_CreateGetInteger( p_demux, "sap-parse" ) )
00341 {
00342
00343 return VLC_EGENERIC;
00344 }
00345
00346
00347 if( p_demux->s )
00348 {
00349 if( stream_Peek( p_demux->s, &p_peek, 7 ) < 7 ) return VLC_EGENERIC;
00350
00351 if( strncmp( (char*)p_peek, "v=0\r\n", 5 ) &&
00352 strncmp( (char*)p_peek, "v=0\n", 4 ) &&
00353 ( p_peek[0] < 'a' || p_peek[0] > 'z' || p_peek[1] != '=' ) )
00354 {
00355 return VLC_EGENERIC;
00356 }
00357 }
00358
00359 psz_sdp = (char *)malloc( i_max_sdp );
00360 if( !psz_sdp ) return VLC_EGENERIC;
00361
00362
00363 for( ;; )
00364 {
00365 int i_read = stream_Read( p_demux->s,
00366 &psz_sdp[i_sdp], i_max_sdp - i_sdp - 1 );
00367
00368 if( i_read < 0 )
00369 {
00370 msg_Err( p_demux, "failed to read SDP" );
00371 goto error;
00372 }
00373
00374 i_sdp += i_read;
00375
00376 if( i_read < i_max_sdp - i_sdp - 1 )
00377 {
00378 psz_sdp[i_sdp] = '\0';
00379 break;
00380 }
00381
00382 i_max_sdp += 1000;
00383 psz_sdp = (char *)realloc( psz_sdp, i_max_sdp );
00384 }
00385
00386 p_sdp = ParseSDP( VLC_OBJECT(p_demux), psz_sdp );
00387
00388 if( !p_sdp )
00389 {
00390 msg_Warn( p_demux, "invalid SDP");
00391 goto error;
00392 }
00393
00394 if( p_sdp->i_media > 1 )
00395 {
00396 goto error;
00397 }
00398
00399 if( ParseConnection( VLC_OBJECT( p_demux ), p_sdp ) )
00400 {
00401 p_sdp->psz_uri = NULL;
00402 }
00403 if( p_sdp->i_media_type != 33 && p_sdp->i_media_type != 32 &&
00404 p_sdp->i_media_type != 14 )
00405 goto error;
00406
00407 if( p_sdp->psz_uri == NULL ) goto error;
00408
00409 p_demux->p_sys = (demux_sys_t *)malloc( sizeof(demux_sys_t) );
00410 p_demux->p_sys->p_sdp = p_sdp;
00411 p_demux->pf_control = Control;
00412 p_demux->pf_demux = Demux;
00413
00414 free( psz_sdp );
00415 return VLC_SUCCESS;
00416
00417 error:
00418 free( psz_sdp );
00419 if( p_sdp ) FreeSDP( p_sdp );
00420 stream_Seek( p_demux->s, 0 );
00421 return VLC_EGENERIC;
00422 }
00423
00424
00425
00426
00427 static void Close( vlc_object_t *p_this )
00428 {
00429 services_discovery_t *p_sd = ( services_discovery_t* )p_this;
00430 services_discovery_sys_t *p_sys = p_sd->p_sys;
00431
00432 int i;
00433
00434 for( i = p_sys->i_fd-1 ; i >= 0 ; i-- )
00435 {
00436 net_Close( p_sys->pi_fd[i] );
00437 }
00438 FREE( p_sys->pi_fd );
00439
00440 if( config_GetInt( p_sd, "sap-cache" ) )
00441 {
00442 CacheSave( p_sd );
00443 }
00444
00445 for( i = p_sys->i_announces - 1; i>= 0; i-- )
00446 {
00447 RemoveAnnounce( p_sd, p_sys->pp_announces[i] );
00448 }
00449 FREE( p_sys->pp_announces );
00450
00451 if( p_sys->p_playlist )
00452 {
00453 playlist_NodeDelete( p_sys->p_playlist, p_sys->p_node, VLC_TRUE,
00454 VLC_TRUE );
00455 vlc_object_release( p_sys->p_playlist );
00456 }
00457
00458 free( p_sys );
00459 }
00460
00461
00462
00463
00464 static void CloseDemux( vlc_object_t *p_this )
00465 {
00466
00467 }
00468
00469
00470
00471
00472
00473
00474 #define MAX_SAP_BUFFER 5000
00475
00476 static void Run( services_discovery_t *p_sd )
00477 {
00478 char *psz_addr;
00479 int i;
00480
00481
00482
00483
00484
00485
00486
00487 if( var_CreateGetInteger( p_sd, "sap-ipv4" ) )
00488 {
00489 InitSocket( p_sd, SAP_V4_GLOBAL_ADDRESS, SAP_PORT );
00490 InitSocket( p_sd, SAP_V4_ORG_ADDRESS, SAP_PORT );
00491 InitSocket( p_sd, SAP_V4_LOCAL_ADDRESS, SAP_PORT );
00492 InitSocket( p_sd, SAP_V4_LINK_ADDRESS, SAP_PORT );
00493 }
00494 if( var_CreateGetInteger( p_sd, "sap-ipv6" ) )
00495 {
00496 char psz_address[] = SAP_V6_1"0"SAP_V6_2;
00497 const char *c_scope;
00498
00499 for( c_scope = ipv6_scopes; *c_scope; c_scope++ )
00500 {
00501 psz_address[sizeof(SAP_V6_1) - 1] = *c_scope;
00502 InitSocket( p_sd, psz_address, SAP_PORT );
00503 }
00504 }
00505
00506 psz_addr = var_CreateGetString( p_sd, "sap-addr" );
00507 if( psz_addr && *psz_addr )
00508 {
00509 InitSocket( p_sd, psz_addr, SAP_PORT );
00510 }
00511
00512 if( p_sd->p_sys->i_fd == 0 )
00513 {
00514 msg_Err( p_sd, "unable to listen on any address" );
00515 return;
00516 }
00517
00518
00519 while( !p_sd->b_die )
00520 {
00521 int i_read;
00522 uint8_t p_buffer[MAX_SAP_BUFFER+1];
00523
00524 i_read = net_Select( p_sd, p_sd->p_sys->pi_fd, NULL,
00525 p_sd->p_sys->i_fd, p_buffer,
00526 MAX_SAP_BUFFER, 500000 );
00527
00528
00529 for( i = 0; i < p_sd->p_sys->i_announces; i++ )
00530 {
00531 mtime_t i_timeout = ( mtime_t ) 1000000 * p_sd->p_sys->i_timeout;
00532
00533 if( mdate() - p_sd->p_sys->pp_announces[i]->i_last > i_timeout )
00534 {
00535 struct sap_announce_t *p_announce;
00536 p_announce = p_sd->p_sys->pp_announces[i];
00537
00538
00539 playlist_LockDelete( p_sd->p_sys->p_playlist,
00540 p_announce->i_item_id );
00541
00542
00543 REMOVE_ELEM( p_sd->p_sys->pp_announces,
00544 p_sd->p_sys->i_announces, i );
00545
00546 free( p_announce );
00547 }
00548 }
00549
00550
00551 if( i_read <= 6 )
00552 {
00553 if( i_read < 0 )
00554 {
00555 msg_Warn( p_sd, "socket read error" );
00556 }
00557 continue;
00558 }
00559
00560 p_buffer[i_read] = '\0';
00561
00562
00563 ParseSAP( p_sd, p_buffer, i_read );
00564 }
00565 }
00566
00567
00568
00569
00570
00571 static int Demux( demux_t *p_demux )
00572 {
00573 sdp_t *p_sdp = p_demux->p_sys->p_sdp;
00574 playlist_t *p_playlist;
00575
00576 p_playlist = (playlist_t *)vlc_object_find( p_demux, VLC_OBJECT_PLAYLIST,
00577 FIND_ANYWHERE );
00578
00579 p_playlist->status.p_item->i_flags |= PLAYLIST_DEL_FLAG;
00580
00581 playlist_Add( p_playlist, p_sdp->psz_uri, p_sdp->psz_sessionname,
00582 PLAYLIST_APPEND, PLAYLIST_END );
00583
00584 vlc_object_release( p_playlist );
00585 if( p_sdp ) FreeSDP( p_sdp );
00586
00587 return VLC_SUCCESS;
00588 }
00589
00590 static int Control( demux_t *p_demux, int i_query, va_list args )
00591 {
00592 return VLC_EGENERIC;
00593 }
00594
00595
00596
00597
00598
00599
00600 static int ParseSAP( services_discovery_t *p_sd, uint8_t *p_buffer, int i_read )
00601 {
00602 int i_version, i_address_type, i_hash, i;
00603 char *psz_sdp, *psz_foo, *psz_initial_sdp;
00604 sdp_t *p_sdp;
00605 vlc_bool_t b_compressed;
00606 vlc_bool_t b_need_delete = VLC_FALSE;
00607
00608
00609 i_version = p_buffer[0] >> 5;
00610 if( i_version != 1 )
00611 {
00612 msg_Dbg( p_sd, "strange sap version %d found", i_version );
00613 }
00614
00615 i_address_type = p_buffer[0] & 0x10;
00616
00617 if( (p_buffer[0] & 0x08) != 0 )
00618 {
00619 msg_Dbg( p_sd, "reserved bit incorrectly set" );
00620 return VLC_EGENERIC;
00621 }
00622
00623 if( (p_buffer[0] & 0x04) != 0 )
00624 {
00625 msg_Dbg( p_sd, "session deletion packet" );
00626 b_need_delete = VLC_TRUE;
00627 }
00628
00629 if( p_buffer[0] & 0x02 )
00630 {
00631 msg_Dbg( p_sd, "encrypted packet, unsupported" );
00632 return VLC_EGENERIC;
00633 }
00634
00635 b_compressed = p_buffer[0] & 0x01;
00636
00637 i_hash = ( p_buffer[2] << 8 ) + p_buffer[3];
00638
00639 if( p_sd->p_sys->b_strict && i_hash == 0 )
00640 {
00641 msg_Dbg( p_sd, "strict mode, discarding announce with null id hash");
00642 return VLC_EGENERIC;
00643 }
00644
00645 psz_sdp = (char *)p_buffer + 4;
00646 psz_initial_sdp = psz_sdp;
00647
00648 if( i_address_type == 0 )
00649 {
00650 psz_sdp += 4;
00651 if( i_read <= 9 )
00652 {
00653 msg_Warn( p_sd, "too short SAP packet\n" );
00654 return VLC_EGENERIC;
00655 }
00656 }
00657 else
00658 {
00659 psz_sdp += 16;
00660 if( i_read <= 21 )
00661 {
00662 msg_Warn( p_sd, "too short SAP packet\n" );
00663 return VLC_EGENERIC;
00664 }
00665 }
00666
00667 if( b_compressed )
00668 {
00669 #ifdef HAVE_ZLIB_H
00670 uint8_t *p_decompressed_buffer = NULL;
00671 int i_decompressed_size;
00672
00673 i_decompressed_size = Decompress( (uint8_t *)psz_sdp,
00674 &p_decompressed_buffer, i_read - ( psz_sdp - (char *)p_buffer ) );
00675 if( i_decompressed_size > 0 && i_decompressed_size < MAX_SAP_BUFFER )
00676 {
00677 memcpy( psz_sdp, p_decompressed_buffer, i_decompressed_size );
00678 psz_sdp[i_decompressed_size] = '\0';
00679 free( p_decompressed_buffer );
00680 }
00681 #else
00682 msg_Warn( p_sd, "Ignoring compressed sap packet" );
00683 return VLC_EGENERIC;
00684 #endif
00685 }
00686
00687
00688 if( i_read < p_buffer[1] + (psz_sdp - psz_initial_sdp ) )
00689 {
00690 msg_Warn( p_sd, "too short SAP packet\n");
00691 return VLC_EGENERIC;
00692 }
00693 psz_sdp += p_buffer[1];
00694 psz_foo = psz_sdp;
00695
00696
00697
00698 while( *psz_sdp != '\0' && ( psz_sdp[0] != 'v' && psz_sdp[1] != '=' ) )
00699 {
00700 if( psz_sdp - psz_initial_sdp >= i_read - 5 )
00701 {
00702 msg_Warn( p_sd, "empty SDP ?");
00703 }
00704 psz_sdp++;
00705 }
00706
00707 if( *psz_sdp == '\0' )
00708 {
00709 psz_sdp++;
00710 }
00711 if( ( psz_sdp != psz_foo ) && strcasecmp( psz_foo, "application/sdp" ) )
00712 {
00713 msg_Dbg( p_sd, "unhandled content type: %s", psz_foo );
00714 }
00715 if( ( psz_sdp - (char *)p_buffer ) >= i_read )
00716 {
00717 msg_Warn( p_sd, "package without content" );
00718 return VLC_EGENERIC;
00719 }
00720
00721
00722 p_sdp = ParseSDP( VLC_OBJECT(p_sd), psz_sdp );
00723
00724 if( p_sdp == NULL )
00725 {
00726 return VLC_EGENERIC;
00727 }
00728
00729
00730
00731 if( ParseConnection( VLC_OBJECT(p_sd), p_sdp ) )
00732 {
00733 p_sdp->psz_uri = NULL;
00734 }
00735
00736
00737 if( p_sdp->i_media > 1 || ( p_sdp->i_media_type != 14 &&
00738 p_sdp->i_media_type != 32 &&
00739 p_sdp->i_media_type != 33) ||
00740 p_sd->p_sys->b_parse == VLC_FALSE )
00741 {
00742 if( p_sdp->psz_uri ) free( p_sdp->psz_uri );
00743 asprintf( &p_sdp->psz_uri, "sdp://%s", p_sdp->psz_sdp );
00744 }
00745
00746 if( p_sdp->psz_uri == NULL ) return VLC_EGENERIC;
00747
00748 for( i = 0 ; i< p_sd->p_sys->i_announces ; i++ )
00749 {
00750
00751
00752 if( IsSameSession( p_sd->p_sys->pp_announces[i]->p_sdp, p_sdp ) )
00753 {
00754 if( b_need_delete )
00755 {
00756 RemoveAnnounce( p_sd, p_sd->p_sys->pp_announces[i]);
00757 return VLC_SUCCESS;
00758 }
00759 else
00760 {
00761 p_sd->p_sys->pp_announces[i]->i_last = mdate();
00762 FreeSDP( p_sdp );
00763 return VLC_SUCCESS;
00764 }
00765 }
00766 }
00767
00768 if( p_sdp->i_media > 1 )
00769 {
00770 msg_Dbg( p_sd, "passing to LIVE.COM" );
00771 }
00772
00773 CreateAnnounce( p_sd, i_hash, p_sdp );
00774
00775 return VLC_SUCCESS;
00776 }
00777
00778 sap_announce_t *CreateAnnounce( services_discovery_t *p_sd, uint16_t i_hash,
00779 sdp_t *p_sdp )
00780 {
00781 playlist_item_t *p_item, *p_child;
00782 char *psz_value;
00783 sap_announce_t *p_sap = (sap_announce_t *)malloc(
00784 sizeof(sap_announce_t ) );
00785 if( p_sap == NULL )
00786 return NULL;
00787
00788 EnsureUTF8( p_sdp->psz_sessionname );
00789 p_sap->i_last = mdate();
00790 p_sap->i_hash = i_hash;
00791 p_sap->p_sdp = p_sdp;
00792 p_sap->i_item_id = -1;
00793
00794
00795 p_item = playlist_ItemNew( p_sd, p_sap->p_sdp->psz_uri,
00796 p_sdp->psz_sessionname );
00797
00798 if( !p_item )
00799 {
00800 free( p_sap );
00801 return NULL;
00802 }
00803
00804 if( p_sd->p_sys->b_timeshift )
00805 playlist_ItemAddOption( p_item, ":access-filter=timeshift" );
00806
00807 psz_value = GetAttribute( p_sap->p_sdp, "tool" );
00808 if( psz_value != NULL )
00809 {
00810 vlc_input_item_AddInfo( &p_item->input, _("Session"),
00811 _("Tool"), psz_value );
00812 }
00813 if( strcmp( p_sdp->psz_username, "-" ) )
00814 {
00815 vlc_input_item_AddInfo( &p_item->input, _("Session"),
00816 _("User"), p_sdp->psz_username );
00817 }
00818
00819 psz_value = GetAttribute( p_sap->p_sdp, "x-plgroup" );
00820
00821 if( psz_value == NULL )
00822 {
00823 psz_value = GetAttribute( p_sap->p_sdp, "plgroup" );
00824 }
00825
00826 if( psz_value != NULL )
00827 {
00828 EnsureUTF8( psz_value );
00829
00830 p_child = playlist_ChildSearchName( p_sd->p_sys->p_node, psz_value );
00831
00832 if( p_child == NULL )
00833 {
00834 p_child = playlist_NodeCreate( p_sd->p_sys->p_playlist,
00835 VIEW_CATEGORY, psz_value,
00836 p_sd->p_sys->p_node );
00837 p_child->i_flags &= ~PLAYLIST_SKIP_FLAG;
00838 }
00839 }
00840 else
00841 {
00842 p_child = p_sd->p_sys->p_node;
00843 }
00844
00845 p_item->i_flags &= ~PLAYLIST_SKIP_FLAG;
00846 p_item->i_flags &= ~PLAYLIST_SAVE_FLAG;
00847
00848 playlist_NodeAddItem( p_sd->p_sys->p_playlist, p_item, VIEW_CATEGORY,
00849 p_child, PLAYLIST_APPEND, PLAYLIST_END );
00850
00851 p_sap->i_item_id = p_item->input.i_id;
00852
00853 TAB_APPEND( p_sd->p_sys->i_announces,
00854 p_sd->p_sys->pp_announces, p_sap );
00855
00856 return p_sap;
00857 }
00858
00859 static char *GetAttribute( sdp_t *p_sdp, const char *psz_search )
00860 {
00861 int i;
00862
00863 for( i = 0 ; i< p_sdp->i_attributes; i++ )
00864 {
00865 if( !strncmp( p_sdp->pp_attributes[i]->psz_field, psz_search,
00866 strlen( p_sdp->pp_attributes[i]->psz_field ) ) )
00867 {
00868 return p_sdp->pp_attributes[i]->psz_value;
00869 }
00870 }
00871 return NULL;
00872 }
00873
00874
00875
00876 static int ParseConnection( vlc_object_t *p_obj, sdp_t *p_sdp )
00877 {
00878 char *psz_eof;
00879 char *psz_parse;
00880 char *psz_uri = NULL;
00881 char *psz_proto = NULL;
00882 char psz_source[256];
00883 int i_port = 0;
00884
00885
00886 if( p_sdp->psz_connection )
00887 {
00888 psz_parse = p_sdp->psz_connection;
00889
00890 psz_eof = strchr( psz_parse, ' ' );
00891
00892 if( psz_eof )
00893 {
00894 *psz_eof = '\0';
00895 psz_parse = psz_eof + 1;
00896 }
00897 else
00898 {
00899 msg_Warn( p_obj, "unable to parse c field (1)");
00900 return VLC_EGENERIC;
00901 }
00902
00903 psz_eof = strchr( psz_parse, ' ' );
00904
00905 if( psz_eof )
00906 {
00907 *psz_eof = '\0';
00908 if( !strncmp( psz_parse, "IP4", 3 ) )
00909 {
00910 p_sdp->i_in = 4;
00911 }
00912 else if( !strncmp( psz_parse, "IP6", 3 ) )
00913 {
00914 p_sdp->i_in = 6;
00915 }
00916 else
00917 {
00918 p_sdp->i_in = 0;
00919 }
00920 psz_parse = psz_eof + 1;
00921 }
00922 else
00923 {
00924 msg_Warn( p_obj, "unable to parse c field (2)");
00925 return VLC_EGENERIC;
00926 }
00927
00928 psz_eof = strchr( psz_parse, '/' );
00929
00930 if( psz_eof )
00931 {
00932 *psz_eof = '\0';
00933 }
00934 else
00935 {
00936 msg_Dbg( p_obj, "incorrect c field, %s", p_sdp->psz_connection );
00937 }
00938 if( p_sdp->i_in == 6 && ( isxdigit( *psz_parse ) || *psz_parse == ':' ) )
00939 {
00940 asprintf( &psz_uri, "[%s]", psz_parse );
00941 }
00942 else psz_uri = strdup( psz_parse );
00943
00944 }
00945
00946
00947 if( p_sdp->psz_media )
00948 {
00949 psz_parse = p_sdp->psz_media;
00950
00951 psz_eof = strchr( psz_parse, ' ' );
00952
00953 if( psz_eof )
00954 {
00955 *psz_eof = '\0';
00956
00957 if( strncmp( psz_parse, "audio", 5 ) &&
00958 strncmp( psz_parse, "video", 5 ) )
00959 {
00960 msg_Warn( p_obj, "unhandled media type -%s-", psz_parse );
00961 FREE( psz_uri );
00962 return VLC_EGENERIC;
00963 }
00964
00965 psz_parse = psz_eof + 1;
00966 }
00967 else
00968 {
00969 msg_Warn( p_obj, "unable to parse m field (1)");
00970 FREE( psz_uri );
00971 return VLC_EGENERIC;
00972 }
00973
00974 psz_eof = strchr( psz_parse, ' ' );
00975
00976 if( psz_eof )
00977 {
00978 *psz_eof = '\0';
00979
00980
00981 i_port = atoi( psz_parse );
00982
00983 if( i_port <= 0 || i_port >= 65536 )
00984 {
00985 msg_Warn( p_obj, "invalid transport port %i", i_port );
00986 }
00987
00988 psz_parse = psz_eof + 1;
00989 }
00990 else
00991 {
00992 msg_Warn( p_obj, "unable to parse m field (2)");
00993 FREE( psz_uri );
00994 return VLC_EGENERIC;
00995 }
00996
00997 psz_eof = strchr( psz_parse, ' ' );
00998
00999 if( psz_eof )
01000 {
01001 *psz_eof = '\0';
01002 psz_proto = strdup( psz_parse );
01003
01004 psz_parse = psz_eof + 1;
01005 p_sdp->i_media_type = atoi( psz_parse );
01006
01007 }
01008 else
01009 {
01010 msg_Dbg( p_obj, "incorrect m field, %s", p_sdp->psz_media );
01011 p_sdp->i_media_type = 33;
01012 psz_proto = strdup( psz_parse );
01013 }
01014 }
01015
01016 if( psz_proto && !strncmp( psz_proto, "RTP/AVP", 7 ) )
01017 {
01018 free( psz_proto );
01019 psz_proto = strdup( "rtp" );
01020 }
01021 if( psz_proto && !strncasecmp( psz_proto, "UDP", 3 ) )
01022 {
01023 free( psz_proto );
01024 psz_proto = strdup( "udp" );
01025 }
01026
01027
01028
01029 if( i_port == 0 )
01030 {
01031 i_port = 1234;
01032 }
01033
01034
01035 psz_parse = GetAttribute( p_sdp, "source-filter" );
01036 psz_source[0] = '\0';
01037
01038 if( psz_parse ) sscanf( psz_parse, " incl IN IP%*s %*s %255s ", psz_source);
01039
01040 asprintf( &p_sdp->psz_uri, "%s://%s@%s:%i", psz_proto, psz_source,
01041 psz_uri, i_port );
01042
01043 FREE( psz_uri );
01044 FREE( psz_proto );
01045 return VLC_SUCCESS;
01046 }
01047
01048
01049
01050
01051
01052
01053 static sdp_t * ParseSDP( vlc_object_t *p_obj, char* psz_sdp )
01054 {
01055 sdp_t *p_sdp;
01056 vlc_bool_t b_invalid = VLC_FALSE;
01057 vlc_bool_t b_end = VLC_FALSE;
01058 if( psz_sdp == NULL )
01059 {
01060 return NULL;
01061 }
01062
01063 if( psz_sdp[0] != 'v' || psz_sdp[1] != '=' )
01064 {
01065 msg_Warn( p_obj, "Bad packet" );
01066 return NULL;
01067 }
01068
01069 p_sdp = (sdp_t *)malloc( sizeof( sdp_t ) );
01070 if( p_sdp == NULL )
01071 return NULL;
01072
01073 p_sdp->psz_sdp = strdup( psz_sdp );
01074 if( p_sdp->psz_sdp == NULL )
01075 {
01076 free( p_sdp );
01077 return NULL;
01078 }
01079
01080 p_sdp->psz_sessionname = NULL;
01081 p_sdp->psz_media = NULL;
01082 p_sdp->psz_connection = NULL;
01083 p_sdp->psz_uri = NULL;
01084 p_sdp->psz_address = NULL;
01085 p_sdp->psz_address_type= NULL;
01086
01087 p_sdp->i_media = 0;
01088 p_sdp->i_attributes = 0;
01089 p_sdp->pp_attributes = NULL;
01090
01091 while( *psz_sdp != '\0' && b_end == VLC_FALSE )
01092 {
01093 char *psz_eol;
01094 char *psz_eof;
01095 char *psz_parse;
01096 char *psz_sess_id;
01097
01098 while( *psz_sdp == '\r' || *psz_sdp == '\n' ||
01099 *psz_sdp == ' ' || *psz_sdp == '\t' )
01100 {
01101 psz_sdp++;
01102 }
01103
01104 if( ( psz_eol = strchr( psz_sdp, '\n' ) ) == NULL )
01105 {
01106 psz_eol = psz_sdp + strlen( psz_sdp );
01107 b_end = VLC_TRUE;
01108 }
01109 if( psz_eol > psz_sdp && *( psz_eol - 1 ) == '\r' )
01110 {
01111 psz_eol--;
01112 }
01113
01114 if( psz_eol <= psz_sdp )
01115 {
01116 break;
01117 }
01118 *psz_eol++ = '\0';
01119
01120
01121 if( psz_sdp[1] != '=' )
01122 {
01123 msg_Warn( p_obj, "invalid packet" ) ;
01124 FreeSDP( p_sdp );
01125 return NULL;
01126 }
01127
01128
01129 switch( psz_sdp[0] )
01130 {
01131 case( 'v' ):
01132 break;
01133 case( 's' ):
01134 p_sdp->psz_sessionname = strdup( &psz_sdp[2] );
01135 break;
01136 case ( 'o' ):
01137 {
01138 int i_field = 0;
01139
01140
01141
01142 #define GET_FIELD( store ) \
01143 psz_eof = strchr( psz_parse, ' ' ); \
01144 if( psz_eof ) \
01145 { \
01146 *psz_eof=0; store = strdup( psz_parse ); \
01147 } \
01148 else \
01149 { \
01150 if( i_field != 5 ) \
01151 { \
01152 b_invalid = VLC_TRUE; break; \
01153 } \
01154 else \
01155 { \
01156 store = strdup( psz_parse ); \
01157 } \
01158 }; \
01159 psz_parse = psz_eof + 1; i_field++;
01160
01161
01162 psz_parse = &psz_sdp[2];
01163 GET_FIELD( p_sdp->psz_username );
01164 GET_FIELD( psz_sess_id );
01165
01166 p_sdp->i_session_id = atoll( psz_sess_id );
01167
01168 FREE( psz_sess_id );
01169
01170 GET_FIELD( psz_sess_id );
01171 FREE( psz_sess_id );
01172
01173 GET_FIELD( p_sdp->psz_network_type );
01174 GET_FIELD( p_sdp->psz_address_type );
01175 GET_FIELD( p_sdp->psz_address );
01176
01177 break;
01178 }
01179 case( 'i' ):
01180 case( 'u' ):
01181 case( 'e' ):
01182 case( 'p' ):
01183 case( 't' ):
01184 case( 'r' ):
01185 break;
01186 case( 'a' ):
01187 {
01188 char *psz_eon = strchr( &psz_sdp[2], ':' );
01189 attribute_t *p_attr = malloc( sizeof( attribute_t ) );
01190
01191
01192 if( psz_eon )
01193 {
01194 *psz_eon++ = '\0';
01195
01196 p_attr->psz_field = strdup( &psz_sdp[2] );
01197 p_attr->psz_value = strdup( psz_eon );
01198 }
01199 else
01200 {
01201 p_attr->psz_field = strdup( &psz_sdp[2] );
01202 p_attr->psz_value = NULL;
01203 }
01204
01205 TAB_APPEND( p_sdp->i_attributes, p_sdp->pp_attributes, p_attr );
01206 break;
01207 }
01208
01209 case( 'm' ):
01210 {
01211
01212
01213 p_sdp->i_media++;
01214 if( p_sdp->i_media == 1 )
01215 {
01216 p_sdp->psz_media = strdup( &psz_sdp[2] );
01217 }
01218 break;
01219 }
01220
01221 case( 'c' ):
01222 {
01223 if( p_sdp->i_media > 1 )
01224 break;
01225
01226 p_sdp->psz_connection = strdup( &psz_sdp[2] );
01227 break;
01228 }
01229
01230 default:
01231 break;
01232 }
01233
01234 if( b_invalid )
01235 {
01236 FreeSDP( p_sdp );
01237 return NULL;
01238 }
01239
01240 psz_sdp = psz_eol;
01241 }
01242
01243 return p_sdp;
01244 }
01245
01246 static int InitSocket( services_discovery_t *p_sd, char *psz_address,
01247 int i_port )
01248 {
01249 int i_fd = net_OpenUDP( p_sd, psz_address, i_port, NULL, 0 );
01250
01251 if( i_fd != -1 )
01252 {
01253 net_StopSend( i_fd );
01254 INSERT_ELEM( p_sd->p_sys->pi_fd, p_sd->p_sys->i_fd,
01255 p_sd->p_sys->i_fd, i_fd );
01256 return VLC_SUCCESS;
01257 }
01258
01259 return VLC_EGENERIC;
01260 }
01261
01262 #ifdef HAVE_ZLIB_H
01263 static int Decompress( unsigned char *psz_src, unsigned char **_dst, int i_len )
01264 {
01265 int i_result, i_dstsize, n;
01266 unsigned char *psz_dst;
01267 z_stream d_stream;
01268
01269 d_stream.zalloc = (alloc_func)0;
01270 d_stream.zfree = (free_func)0;
01271 d_stream.opaque = (voidpf)0;
01272
01273 i_result = inflateInit(&d_stream);
01274 if( i_result != Z_OK )
01275 {
01276 printf( "inflateInit() failed. Result: %d\n", i_result );
01277 return( -1 );
01278 }
01279 #if 0
01280 p_playlist->pp_items[p_playlist->i_index]->b_autodeletion = VLC_TRUE;
01281 i_position = p_playlist->i_index;
01282
01283
01284 for( ;; )
01285 {
01286 int i_read = stream_Read( p_demux->s, &p_sdp[i_sdp], i_sdp_max - i_sdp - 1 );
01287 #endif
01288 d_stream.next_in = (Bytef *)psz_src;
01289 d_stream.avail_in = i_len;
01290 n = 0;
01291
01292 psz_dst = NULL;
01293
01294 do
01295 {
01296 n++;
01297 psz_dst = (unsigned char *)realloc( psz_dst, n * 1000 );
01298 d_stream.next_out = (Bytef *)&psz_dst[(n - 1) * 1000];
01299 d_stream.avail_out = 1000;
01300
01301 i_result = inflate(&d_stream, Z_NO_FLUSH);
01302 if( ( i_result != Z_OK ) && ( i_result != Z_STREAM_END ) )
01303 {
01304 printf( "Zlib decompression failed. Result: %d\n", i_result );
01305 return( -1 );
01306 }
01307 }
01308 while( ( d_stream.avail_out == 0 ) && ( d_stream.avail_in != 0 ) &&
01309 ( i_result != Z_STREAM_END ) );
01310
01311 i_dstsize = d_stream.total_out;
01312 inflateEnd( &d_stream );
01313
01314 *_dst = (unsigned char *)realloc( psz_dst, i_dstsize );
01315
01316 return i_dstsize;
01317 }
01318 #endif
01319
01320
01321 static void FreeSDP( sdp_t *p_sdp )
01322 {
01323 int i;
01324 FREE( p_sdp->psz_sdp );
01325 FREE( p_sdp->psz_sessionname );
01326 FREE( p_sdp->psz_connection );
01327 FREE( p_sdp->psz_media );
01328 FREE( p_sdp->psz_uri );
01329 FREE( p_sdp->psz_username );
01330 FREE( p_sdp->psz_network_type );
01331
01332 FREE( p_sdp->psz_address );
01333 FREE( p_sdp->psz_address_type );
01334
01335 for( i= p_sdp->i_attributes - 1; i >= 0 ; i-- )
01336 {
01337 struct attribute_t *p_attr = p_sdp->pp_attributes[i];
01338 FREE( p_sdp->pp_attributes[i]->psz_field );
01339 FREE( p_sdp->pp_attributes[i]->psz_value );
01340 REMOVE_ELEM( p_sdp->pp_attributes, p_sdp->i_attributes, i);
01341 FREE( p_attr );
01342 }
01343 free( p_sdp );
01344 }
01345
01346 static int RemoveAnnounce( services_discovery_t *p_sd,
01347 sap_announce_t *p_announce )
01348 {
01349 int i;
01350
01351 if( p_announce->p_sdp ) FreeSDP( p_announce->p_sdp );
01352
01353 if( p_announce->i_item_id > -1 )
01354 {
01355 playlist_LockDelete( p_sd->p_sys->p_playlist, p_announce->i_item_id );
01356 }
01357
01358 for( i = 0; i< p_sd->p_sys->i_announces; i++)
01359 {
01360 if( p_sd->p_sys->pp_announces[i] == p_announce )
01361 {
01362 REMOVE_ELEM( p_sd->p_sys->pp_announces, p_sd->p_sys->i_announces,
01363 i);
01364 break;
01365 }
01366 }
01367
01368 free( p_announce );
01369
01370 return VLC_SUCCESS;
01371 }
01372
01373 static vlc_bool_t IsSameSession( sdp_t *p_sdp1, sdp_t *p_sdp2 )
01374 {
01375
01376
01377 if( p_sdp1->psz_username && p_sdp2->psz_username &&
01378 p_sdp1->psz_network_type && p_sdp2->psz_network_type &&
01379 p_sdp1->psz_address_type && p_sdp2->psz_address_type &&
01380 p_sdp1->psz_address && p_sdp2->psz_address )
01381 {
01382 if(!strcmp( p_sdp1->psz_username , p_sdp2->psz_username ) &&
01383 !strcmp( p_sdp1->psz_network_type , p_sdp2->psz_network_type ) &&
01384 !strcmp( p_sdp1->psz_address_type , p_sdp2->psz_address_type ) &&
01385 !strcmp( p_sdp1->psz_address , p_sdp2->psz_address ) &&
01386 p_sdp1->i_session_id == p_sdp2->i_session_id )
01387 {
01388 return VLC_TRUE;
01389 }
01390 else
01391 {
01392 return VLC_FALSE;
01393 }
01394 }
01395 else
01396 {
01397 return VLC_FALSE;
01398 }
01399 }
01400
01401
01402 static void CacheLoad( services_discovery_t *p_sd )
01403 {
01404 msg_Warn( p_sd, "Cache not implemented") ;
01405 }
01406
01407 static void CacheSave( services_discovery_t *p_sd )
01408 {
01409 msg_Warn( p_sd, "Cache not implemented") ;
01410 }