Commit fa55e4cf authored by Clément Stenac's avatar Clément Stenac

rewrite of the SAP parser

parent f9d5e2ba
/***************************************************************************** /*****************************************************************************
* sap.c : SAP interface module * sap.c : SAP interface module
***************************************************************************** *****************************************************************************
* Copyright (C) 2001 VideoLAN * Copyright (C) 2004 VideoLAN
* $Id$ * $Id$
* *
* Authors: Arnaud Schauly <gitan@via.ecp.fr> * Authors: Clment Stenac <zorglub@videolan.org>
* Clment Stenac <zorglub@via.ecp.fr>
* Damien Lucas <nitrox@videolan.org>
* Laurent Aimar <fenrir@via.ecp.fr>
* *
* This program is free software; you can redistribute it and/or modify * 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 * it under the terms of the GNU General Public License as published by
...@@ -25,16 +22,18 @@ ...@@ -25,16 +22,18 @@
*****************************************************************************/ *****************************************************************************/
/***************************************************************************** /*****************************************************************************
* Preamble * Includes
*****************************************************************************/ *****************************************************************************/
#include <stdlib.h> /* malloc(), free() */ #include <stdlib.h> /* malloc(), free() */
#include <vlc/vlc.h> #include <vlc/vlc.h>
#include <vlc/intf.h> #include <vlc/intf.h>
#include <vlc/input.h> #include <vlc/input.h>
#include "network.h"
#include <errno.h> /* ENOMEM */ #include <errno.h> /* ENOMEM */
#include <ctype.h>
#ifdef HAVE_UNISTD_H #ifdef HAVE_UNISTD_H
# include <unistd.h> # include <unistd.h>
...@@ -43,39 +42,19 @@ ...@@ -43,39 +42,19 @@
# include <sys/time.h> # include <sys/time.h>
#endif #endif
#ifdef WIN32
# include <winsock2.h>
# include <ws2tcpip.h>
# ifndef IN_MULTICAST
# define IN_MULTICAST(a) IN_CLASSD(a)
# endif
#else
# include <sys/socket.h>
# include <netinet/in.h>
# if HAVE_ARPA_INET_H
# include <arpa/inet.h>
# elif defined( SYS_BEOS )
# include <net/netdb.h>
# endif
#endif
#ifdef UNDER_CE
# define close(a) CloseHandle(a)
#elif defined( WIN32 )
# define close(a) closesocket(a)
#endif
#include "network.h"
#ifdef HAVE_ZLIB_H #ifdef HAVE_ZLIB_H
# include <zlib.h> # include <zlib.h>
#endif #endif
/************************************************************************
* Macros and definitions
************************************************************************/
#define MAX_LINE_LENGTH 256 #define MAX_LINE_LENGTH 256
/* SAP is always on that port */ /* SAP is always on that port */
#define HELLO_PORT 9875 #define SAP_PORT 9875
#define HELLO_GROUP "224.2.127.254" #define SAP_V4_ADDRESS "224.2.127.254"
#define ADD_SESSION 1 #define ADD_SESSION 1
#define IPV6_ADDR_1 "FF0" /* Scope is inserted between them */ #define IPV6_ADDR_1 "FF0" /* Scope is inserted between them */
...@@ -102,13 +81,14 @@ ...@@ -102,13 +81,14 @@
"is received." ) "is received." )
#define SAP_PARSE_TEXT N_( "Try to parse the SAP" ) #define SAP_PARSE_TEXT N_( "Try to parse the SAP" )
#define SAP_PARSE_LONGTEXT N_( \ #define SAP_PARSE_LONGTEXT N_( \
"When SAP can it will try to parse the SAP. Normal behavior is " \ "When SAP can it will try to parse the SAP. If you don't select " \
"to have livedotcom parse the announce." ) "this, all announces will be parsed by the livedotcom module" )
static int Open ( vlc_object_t * ); /* Callbacks */
static void Close( vlc_object_t * ); static int Open ( vlc_object_t * );
static int OpenSDP ( vlc_object_t * ); static void Close( vlc_object_t * );
static void CloseSDP( vlc_object_t * ); static int OpenDemux ( vlc_object_t * );
static void CloseDemux ( vlc_object_t * );
vlc_module_begin(); vlc_module_begin();
set_description( _("SAP interface") ); set_description( _("SAP interface") );
...@@ -130,61 +110,45 @@ vlc_module_begin(); ...@@ -130,61 +110,45 @@ vlc_module_begin();
set_callbacks( Open, Close ); set_callbacks( Open, Close );
add_submodule(); add_submodule();
/* TODO FIXME set_description( _("SDP file parser for UDP") );
* This submodule is temporary and has a very high duplicate code rate
* This needs to be removed when SDP UDP parsing is fixed in liveMedia
*/
set_description( _("SDP file parser (UDP only)") );
add_shortcut( "sdp" ); add_shortcut( "sdp" );
set_capability( "demux2", 51 ); set_capability( "demux2", 51 );
set_callbacks( OpenSDP, CloseSDP ); set_callbacks( OpenDemux, CloseDemux );
vlc_module_end(); vlc_module_end();
/***************************************************************************** /*****************************************************************************
* Local prototypes * Local structures
*****************************************************************************/ *****************************************************************************/
static void Run ( intf_thread_t *p_intf ); typedef struct sdp_t sdp_t;
static ssize_t NetRead( intf_thread_t *, int fd[2], uint8_t *, int ); typedef struct attribute_t attribute_t;
typedef struct sap_announce_t sap_announce_t;
static int Demux( demux_t *p_demux ); /* The structure that contains sdp information */
static int Control( demux_t *, int, va_list ); struct sdp_t
{
typedef struct media_descr_t media_descr_t; char *psz_sdp;
typedef struct sess_descr_t sess_descr_t;
typedef struct attr_descr_t attr_descr_t;
static void sess_toitem( intf_thread_t *, sess_descr_t * ); /* s= field */
char *psz_sessionname;
static sess_descr_t * parse_sdp( vlc_object_t *, char * ) ; /* Raw m= and c= fields */
static void free_sd( sess_descr_t * ); char *psz_connection;
char *psz_media;
/* Detect multicast addresses */ /* "computed" URI */
static int ismult( char * ); char *psz_uri;
/* The struct that contains sdp information */ int i_in; /* IP version */
struct sess_descr_t
{
int i_version;
char *psz_sessionname;
char *psz_connection;
char *psz_sdp;
int i_media; int i_media;
media_descr_t **pp_media;
int i_attributes;
attr_descr_t **pp_attributes;
};
/* All this information is not useful yet. */ int i_attributes;
struct media_descr_t attribute_t **pp_attributes;
{
char *psz_medianame;
char *psz_mediaconnection;
}; };
struct attr_descr_t struct attribute_t
{ {
char *psz_field; char *psz_field;
char *psz_value; char *psz_value;
...@@ -193,71 +157,65 @@ struct attr_descr_t ...@@ -193,71 +157,65 @@ struct attr_descr_t
struct sap_announce_t struct sap_announce_t
{ {
mtime_t i_last; mtime_t i_last;
int i_id;
char *psz_name; uint16_t i_hash;
char *psz_uri; uint32_t i_source[4];
/* SAP annnounces must only contain one SDP */
sdp_t *p_sdp;
playlist_item_t *p_item;
}; };
struct intf_sys_t struct intf_sys_t
{ {
/* IPV4 and IPV6 */ /* Socket descriptors */
int fd[2]; int i_fd;
int *pi_fd;
/* playlist group */ /* playlist node */
int i_group; playlist_item_t *p_node;
/* Table of announces */ /* Table of announces */
int i_announces; int i_announces;
struct sap_announce_t **pp_announces; struct sap_announce_t **pp_announces;
/* Modes */
vlc_bool_t b_strict;
vlc_bool_t b_parse;
int i_timeout; int i_timeout;
}; };
#ifdef HAVE_ZLIB_H /*****************************************************************************
int do_decompress( unsigned char *src, unsigned char **_dst, int slen ) { * Local prototypes
int result, dstsize, n; *****************************************************************************/
unsigned char *dst;
z_stream d_stream;
d_stream.zalloc = (alloc_func)0;
d_stream.zfree = (free_func)0;
d_stream.opaque = (voidpf)0;
result = inflateInit(&d_stream);
if( result != Z_OK )
{
printf( "inflateInit() failed. Result: %d\n", result );
return( -1 );
}
d_stream.next_in = (Bytef *)src;
d_stream.avail_in = slen;
n = 0;
dst = NULL;
do
{
n++;
dst = (unsigned char *)realloc(dst, n * 1000);
d_stream.next_out = (Bytef *)&dst[(n - 1) * 1000];
d_stream.avail_out = 1000;
result = inflate(&d_stream, Z_NO_FLUSH);
if( ( result != Z_OK ) && ( result != Z_STREAM_END ) )
{
printf( "Zlib decompression failed. Result: %d\n", result );
return( -1 );
}
}
while( ( d_stream.avail_out == 0 ) && ( d_stream.avail_in != 0 ) &&
( result != Z_STREAM_END ) );
dstsize = d_stream.total_out; /* Main functions */
inflateEnd( &d_stream ); static int Demux( demux_t *p_demux );
static int Control( demux_t *, int, va_list );
static void Run ( intf_thread_t *p_intf );
*_dst = (unsigned char *)realloc( dst, dstsize ); /* Main parsing functions */
static int ParseConnection( vlc_object_t *p_obj, sdp_t *p_sdp );
static int ParseSAP( intf_thread_t *p_intf, uint8_t *p_buffer, int i_read );
static sdp_t * ParseSDP( vlc_object_t *p_intf, char* psz_sdp );
static sap_announce_t *CreateAnnounce( intf_thread_t *, uint16_t, sdp_t * );
return dstsize; /* Cache */
} static void CacheLoad( intf_thread_t *p_intf );
static void CacheSave( intf_thread_t *p_intf );
/* Helper functions */
static char *GetAttribute( sdp_t *p_sdp, const char *psz_search );
static int InitSocket( intf_thread_t *p_intf, char *psz_address, int i_port );
#ifdef HAVE_ZLIB_H
static int Decompress( unsigned char *psz_src, unsigned char **_dst, int i_len );
#endif #endif
/* Detect multicast addresses */
static int ismult( char * );
/***************************************************************************** /*****************************************************************************
* Open: initialize and create stuff * Open: initialize and create stuff
*****************************************************************************/ *****************************************************************************/
...@@ -267,98 +225,87 @@ static int Open( vlc_object_t *p_this ) ...@@ -267,98 +225,87 @@ static int Open( vlc_object_t *p_this )
intf_sys_t *p_sys = malloc( sizeof( intf_sys_t ) ); intf_sys_t *p_sys = malloc( sizeof( intf_sys_t ) );
playlist_t *p_playlist; playlist_t *p_playlist;
playlist_view_t *p_view;
p_sys->i_timeout = config_GetInt( p_intf,"sap-timeout" ); p_sys->i_timeout = config_GetInt( p_intf,"sap-timeout" );
p_sys->fd[0] = -1;
p_sys->fd[1] = -1;
if( config_GetInt( p_intf, "sap-ipv4" ) )
{
char *psz_address = config_GetPsz( p_intf, "sap-addr" );
network_socket_t sock;
module_t *p_network;
if( psz_address == NULL || *psz_address == '\0' )
{
psz_address = strdup( HELLO_GROUP );
}
/* Prepare the network_socket_t structure */
sock.i_type = NETWORK_UDP;
sock.psz_bind_addr = psz_address;
sock.i_bind_port = HELLO_PORT;
sock.psz_server_addr = "";
sock.i_server_port = 0;
sock.i_ttl = 0;
p_intf->p_private = ( void* ) &sock;
p_network = module_Need( p_intf, "network", "ipv4", VLC_TRUE ); p_intf->pf_run = Run;
if( p_network ) p_intf->p_sys = p_sys;
{
p_sys->fd[0] = sock.i_handle;
module_Unneed( p_intf, p_network );
}
else
{
msg_Warn( p_intf, "failed to open %s:%d", psz_address, HELLO_PORT );
}
free( psz_address );
}
if( config_GetInt( p_intf, "sap-ipv6" ) ) p_sys->pi_fd = NULL;
{ p_sys->i_fd = 0;
char psz_address[100];
char *psz_scope = config_GetPsz( p_intf, "sap-ipv6-scope" );
network_socket_t sock;
module_t *p_network;
if( psz_scope == NULL || *psz_scope == '\0' ) if( config_GetInt( p_intf, "sap-use-cache" ) )
{ {
psz_scope = strdup( "8" ); CacheLoad( p_intf );
} }
snprintf( psz_address, 100, "[%s%c%s]",IPV6_ADDR_1,
psz_scope[0], IPV6_ADDR_2 );
free( psz_scope );
sock.i_type = NETWORK_UDP;
sock.psz_bind_addr = psz_address;
sock.i_bind_port = HELLO_PORT;
sock.psz_server_addr = "";
sock.i_server_port = 0;
sock.i_ttl = 0;
p_intf->p_private = (void*) &sock;
p_network = module_Need( p_intf, "network", "ipv6", VLC_TRUE ); if( config_GetInt( p_intf, "sap-ipv4" ) )
if( p_network )
{ {
p_sys->fd[1] = sock.i_handle; InitSocket( p_intf, SAP_V4_ADDRESS, SAP_PORT );
module_Unneed( p_intf, p_network );
} }
else if( config_GetInt( p_intf, "sap-ipv6" ) )
{ {
msg_Warn( p_intf, "failed to open %s:%d", psz_address, HELLO_PORT ); /* TODO */
}
} }
if( p_sys->fd[0] <= 0 && p_sys->fd[1] <= 0 )
/* TODO : Handle additionnal adresses */
if( p_sys->i_fd == 0 )
{ {
msg_Warn( p_intf, "IPV4 and IPV6 failed" ); msg_Err( p_intf, "unable to read on any address");
free( p_sys );
return VLC_EGENERIC; return VLC_EGENERIC;
} }
/* Create our playlist group */ /* Create our playlist node */
p_playlist = (playlist_t *)vlc_object_find( p_intf, VLC_OBJECT_PLAYLIST, p_playlist = (playlist_t *)vlc_object_find( p_intf, VLC_OBJECT_PLAYLIST,
FIND_ANYWHERE ); FIND_ANYWHERE );
if( p_playlist ) if( !p_playlist )
{ {
playlist_group_t *p_group = playlist_CreateGroup( p_playlist , "SAP" ); msg_Warn( p_intf, "unable to find playlist, cancelling SAP listening");
p_sys->i_group = p_group->i_id; return VLC_EGENERIC;
vlc_object_release( p_playlist );
} }
p_view = playlist_ViewFind( p_playlist, VIEW_CATEGORY );
p_sys->p_node = playlist_NodeCreate( p_playlist, VIEW_CATEGORY,
_("SAP"), p_view->p_root );
vlc_object_release( p_playlist );
p_sys->i_announces = 0; p_sys->i_announces = 0;
p_sys->pp_announces = NULL; p_sys->pp_announces = NULL;
p_intf->pf_run = Run; return VLC_SUCCESS;
p_intf->p_sys = p_sys; }
/*****************************************************************************
* OpenDemux: initialize and create stuff
*****************************************************************************/
static int OpenDemux( vlc_object_t *p_this )
{
demux_t *p_demux = (demux_t *)p_this;
uint8_t *p_peek;
/* Probe for SDP */
if( p_demux->s )
{
if( stream_Peek( p_demux->s, &p_peek, 7 ) < 7 )
{
msg_Err( p_demux, "cannot peek" );
return VLC_EGENERIC;
}
if( strncmp( (char*)p_peek, "v=0\r\n", 5 ) &&
strncmp( (char*)p_peek, "v=0\n", 4 ) &&
( p_peek[0] < 'a' || p_peek[0] > 'z' || p_peek[1] != '=' ) )
{
msg_Warn( p_demux, "SDP (UDP) module discarded" );
return VLC_EGENERIC;
}
}
p_demux->pf_control = Control;
p_demux->pf_demux = Demux;
return VLC_SUCCESS; return VLC_SUCCESS;
} }
...@@ -372,30 +319,34 @@ static void Close( vlc_object_t *p_this ) ...@@ -372,30 +319,34 @@ static void Close( vlc_object_t *p_this )
intf_sys_t *p_sys = p_intf->p_sys; intf_sys_t *p_sys = p_intf->p_sys;
int i; int i;
if( p_sys->fd[0] > 0 ) for( i = p_sys->i_fd-1 ; i >= 0 ; i-- )
{ {
close( p_sys->fd[0] ); net_Close( p_sys->pi_fd[i] );
} }
if( p_sys->fd[1] > 0 )
if( config_GetInt( p_intf, "sap-use-cache" ) )
{ {
close( p_sys->fd[1] ); CacheSave( p_intf );
} }
for( i = 0 ; i< p_sys->i_announces ; i++ ) for( i = p_sys->i_announces - 1; i>= 0; i-- )
{ {
if( p_sys->pp_announces[i]->psz_name ) RemoveAnnounce( p_intf, p_sys->pp_announces[i] );
free( p_sys->pp_announces[i]->psz_name );
if( p_sys->pp_announces[i]->psz_uri )
free( p_sys->pp_announces[i]->psz_uri );
free( p_sys->pp_announces[i] );
} }
free( p_sys->pp_announces );
free( p_sys ); free( p_sys );
} }
/***************************************************************************** /*****************************************************************************
* Run: sap thread * CloseDemux: Close the demuxer
*****************************************************************************/
static void CloseDemux( vlc_object_t *p_this )
{
}
/*****************************************************************************
* Run: main SAP thread
***************************************************************************** *****************************************************************************
* Listens to SAP packets, and sends them to packet_handle * Listens to SAP packets, and sends them to packet_handle
*****************************************************************************/ *****************************************************************************/
...@@ -403,29 +354,27 @@ static void Close( vlc_object_t *p_this ) ...@@ -403,29 +354,27 @@ static void Close( vlc_object_t *p_this )
static void Run( intf_thread_t *p_intf ) static void Run( intf_thread_t *p_intf )
{ {
intf_sys_t *p_sys = p_intf->p_sys; uint8_t *p_buffer;
uint8_t buffer[MAX_SAP_BUFFER + 1];
uint8_t *p_end;
/* Dirty hack to slow down the startup of the sap interface */ /* Dirty hack to slow down the startup of the sap interface */
msleep( 500000 ); /* Unneeded now : our node is in no_select mode */
// msleep( 500000 );
/* read SAP packets */ /* read SAP packets */
while( !p_intf->b_die ) while( !p_intf->b_die )
{ {
playlist_t *p_playlist= NULL; p_buffer = (uint8_t *)malloc( MAX_SAP_BUFFER );
int i;
int i_read = NetRead( p_intf, p_sys->fd, buffer, MAX_SAP_BUFFER ); if( !p_buffer )
uint8_t *p_sdp; {
int i_version; msg_Err( p_intf, "out of memory");
int i_address_type; p_intf->b_die = VLC_TRUE;
int b_reserved; continue;
int b_message_type; }
int b_encrypted;
int b_compressed;
unsigned char *p_decompressed_buffer;
int i_decompressed_size;
int i_read = net_Select( p_intf, p_intf->p_sys->pi_fd,
p_intf->p_sys->i_fd, p_buffer,
MAX_SAP_BUFFER, 500000 );
#if 0
/* Check for items that need deletion */ /* Check for items that need deletion */
for( i = 0 ; i< p_intf->p_sys->i_announces ; i++ ) for( i = 0 ; i< p_intf->p_sys->i_announces ; i++ )
{ {
...@@ -464,562 +413,556 @@ static void Run( intf_thread_t *p_intf ) ...@@ -464,562 +413,556 @@ static void Run( intf_thread_t *p_intf )
} }
} }
#endif
/* Minimum length is > 6 */ /* Minimum length is > 6 */
if( i_read <= 6 ) if( i_read <= 6 )
{ {
if( i_read < 0 ) if( i_read < 0 )
{ {
msg_Warn( p_intf, "Cannot read in the socket" ); msg_Warn( p_intf, "socket read error" );
} }
continue; continue;
} }
buffer[i_read] = '\0'; p_buffer[i_read] = '\0';
p_end = &buffer[i_read];
/* Parse the packet */
ParseSAP( p_intf, p_buffer, i_read );
}
}
/**********************************************************************
* Demux: reads and demuxes data packets
* Return -1 if error, 0 if EOF, 1 else
**********************************************************************/
static int Demux( demux_t *p_demux )
{
int i_max_sdp = 1024;
int i_sdp = 0;
char *psz_sdp = (char *)malloc( i_max_sdp );
sdp_t *p_sdp;
playlist_t *p_playlist;
/* Gather the complete sdp file */
for( ;; )
{
int i_read = stream_Read( p_demux->s,
&psz_sdp[i_sdp], i_max_sdp - i_sdp - 1 );
if( i_read < 0 )
{
msg_Err( p_demux, "failed to read SDP" );
return VLC_EGENERIC;
}
i_sdp += i_read;
if( i_read < i_max_sdp - i_sdp - 1 )
{
psz_sdp[i_sdp] = '\0';
break;
}
i_max_sdp += 1000;
psz_sdp = (uint8_t*)realloc( psz_sdp, i_max_sdp );
}
p_sdp = ParseSDP( VLC_OBJECT(p_demux), psz_sdp );
if( !p_sdp ) return -1;
if( p_sdp->i_media > 1 )
{
return -1;
}
if( ParseConnection( VLC_OBJECT( p_demux ), p_sdp ) )
{
p_sdp->psz_uri = NULL;
}
if( p_sdp->psz_uri == NULL ) return VLC_EGENERIC;
p_playlist = (playlist_t *)vlc_object_find( p_demux, VLC_OBJECT_PLAYLIST,
FIND_ANYWHERE );
playlist_Add( p_playlist, p_sdp->psz_uri, p_sdp->psz_sessionname,
PLAYLIST_APPEND, PLAYLIST_END );
vlc_object_release( p_playlist );
return VLC_SUCCESS;
}
static int Control( demux_t *p_demux, int i_query, va_list args )
{
return VLC_EGENERIC;
}
/**************************************************************
* Local functions
**************************************************************/
static int ParseSAP( intf_thread_t *p_intf, uint8_t *p_buffer, int i_read )
{
int i_version, i_address_type, i_hash, i;
uint8_t *psz_sdp;
sdp_t *p_sdp;
vlc_bool_t b_compressed;
vlc_bool_t b_need_delete = VLC_FALSE;
#ifdef HAVE_ZLIB_H
int i_decompressed_size;
uint8_t *p_decompressed_buffer;
#endif
/* First, check the sap announce is correct */
i_version = p_buffer[0] >> 5;
/* Parse the SAP header */
i_version = buffer[0] >> 5;
if( i_version != 1 ) if( i_version != 1 )
{ {
msg_Dbg( p_intf, "strange sap version %d found", i_version ); msg_Dbg( p_intf, "strange sap version %d found", i_version );
} }
i_address_type = buffer[0] & 0x10;
b_reserved = buffer[0] & 0x08; i_address_type = p_buffer[0] & 0x10;
if( b_reserved != 0 )
if( (p_buffer[0] & 0x08) != 0 )
{ {
msg_Dbg( p_intf, "reserved bit incorrectly set" ); msg_Dbg( p_intf, "reserved bit incorrectly set" );
return VLC_EGENERIC;
}
if( (p_buffer[0] & 0x04) != 0 )
{
msg_Dbg( p_intf, "session deletion packet" );
b_need_delete = VLC_TRUE;
} }
b_message_type = buffer[0] & 0x04;
if( b_message_type != 0 ) if( p_buffer[0] & 0x02 )
{ {
msg_Dbg( p_intf, "got session deletion packet" ); msg_Dbg( p_intf, "encrypted packet, unsupported" );
return VLC_EGENERIC;
} }
b_encrypted = buffer[0] & 0x02;
if( b_encrypted ) b_compressed = p_buffer[0] & 0x01;
i_hash = ( p_buffer[2] << 8 ) + p_buffer[3];
if( p_intf->p_sys->b_strict && i_hash == 0 )
{ {
msg_Dbg( p_intf, "encrypted packet" ); msg_Dbg( p_intf, "strict mode, discarding announce with null id hash");
return VLC_EGENERIC;
} }
b_compressed = buffer[0] & 0x01;
p_sdp = &buffer[4]; psz_sdp = &p_buffer[4];
if( i_address_type == 0 ) /* ipv4 source address */ if( i_address_type == 0 ) /* ipv4 source address */
{ {
p_sdp += 4; psz_sdp += 4;
} }
else /* ipv6 source address */ else /* ipv6 source address */
{ {
p_sdp += 16; psz_sdp += 16;
} }
if( b_compressed ) if( b_compressed )
{ {
#ifdef HAVE_ZLIB_H #ifdef HAVE_ZLIB_H
i_decompressed_size = do_decompress( p_sdp, &p_decompressed_buffer, i_decompressed_size = Decompress( psz_sdp,
i_read - ( p_sdp - buffer ) ); &p_decompressed_buffer,i_read - ( psz_sdp - p_buffer ) );
if( i_decompressed_size > 0 && i_decompressed_size < MAX_SAP_BUFFER ) if( i_decompressed_size > 0 && i_decompressed_size < MAX_SAP_BUFFER )
{ {
memcpy( p_sdp, p_decompressed_buffer, i_decompressed_size ); memcpy( psz_sdp, p_decompressed_buffer, i_decompressed_size );
p_sdp[i_decompressed_size] = '\0'; psz_sdp[i_decompressed_size] = '\0';
p_end = &p_sdp[i_decompressed_size];
free( p_decompressed_buffer ); free( p_decompressed_buffer );
} }
#else #else
msg_Warn( p_intf, "Ignoring compressed sap packet" ); msg_Warn( p_intf, "Ignoring compressed sap packet" );
return VLC_EGENERIC;
#endif #endif
} }
p_sdp += buffer[1]; /* size of signature */
while( p_sdp < p_end - 1 && *p_sdp != '\0' && p_sdp[0] != 'v' && p_sdp[1] != '=' ) /* Add the size of authentification info */
psz_sdp += p_buffer[1];
/* Skip payload type */
/* Handle announces without \0 between SAP and SDP */
while( *psz_sdp != '\0' && ( psz_sdp[0] != 'v' && psz_sdp[1] != '=' ) )
{ {
p_sdp++; psz_sdp++;
} }
if( *p_sdp == '\0' )
if( *psz_sdp == '\0' )
{ {
p_sdp++; psz_sdp++;
} }
if( p_sdp < p_end ) fprintf(stderr,"\n\n%s\n\n", psz_sdp);
{
sess_descr_t *p_sd = parse_sdp( (vlc_object_t *)p_intf, p_sdp ); /* Parse SDP info */
if( p_sd ) p_sdp = ParseSDP( VLC_OBJECT(p_intf), psz_sdp );
if( p_sdp == NULL )
{ {
sess_toitem ( p_intf, p_sd ); return VLC_EGENERIC;
free_sd ( p_sd );
} }
/* Decide whether we should add a playlist item for this SDP */
/* Multi-media or no-parse -> pass to LIVE.COM */
if( p_sdp->i_media > 1 || p_intf->p_sys->b_parse == VLC_FALSE )
{
asprintf( &p_sdp->psz_uri, "sdp://%s", p_sdp->psz_sdp );
} }
else else
{ {
msg_Warn( p_intf, "ditching sap packet" ); /* Parse connection information (c= & m= ) */
if( ParseConnection( VLC_OBJECT(p_intf), p_sdp ) )
{
p_sdp->psz_uri = NULL;
} }
memset( buffer, 0, MAX_SAP_BUFFER );
} }
}
/********************************************************************** if( p_sdp->psz_uri == NULL ) return VLC_EGENERIC;
* cfield_parse
*********************************************************************
* put into *ppsz_uri, the the uri in the cfield, psz_cfield.
*********************************************************************/
static void cfield_parse( char *psz_cfield, char **ppsz_uri ) for( i = 0 ; i< p_intf->p_sys->i_announces ; i++ )
{
char *psz_pos;
if( psz_cfield )
{ {
psz_pos = psz_cfield; /* FIXME: slow */
/* FIXME: we create a new announce each time the sdp changes */
while( *psz_pos != ' ' && *psz_pos !='\0' ) if( !strcmp( p_intf->p_sys->pp_announces[i]->p_sdp->psz_sdp,
p_sdp->psz_sdp ) )
{ {
psz_pos++; if( b_need_delete )
}
psz_pos++;
while( *psz_pos != ' ' && *psz_pos !='\0' )
{ {
psz_pos++; RemoveAnnounce( p_intf, p_intf->p_sys->pp_announces[i]);
return VLC_SUCCESS;
} }
psz_pos++; else
*ppsz_uri = psz_pos;
while( *psz_pos != ' ' && *psz_pos !='/'
&& *psz_pos != '\0' )
{ {
psz_pos++; p_intf->p_sys->pp_announces[i]->i_last = mdate();
} }
*psz_pos = '\0'; return VLC_SUCCESS;
} }
else }
/* Add item */
if( p_sdp->i_media > 1 )
{ {
ppsz_uri = NULL; msg_Dbg( p_intf, "passing to LIVE.COM" );
} }
return; CreateAnnounce( p_intf, i_hash, p_sdp );
}
/**********************************************************************
* mfield_parse
*********************************************************************
* put into *ppsz_proto, and *ppsz_port, the protocol and the port.
*********************************************************************/
return VLC_SUCCESS;
}
static void mfield_parse( char *psz_mfield, char **ppsz_proto, sap_announce_t *CreateAnnounce( intf_thread_t *p_intf, uint16_t i_hash,
char **ppsz_port ) sdp_t *p_sdp )
{ {
char *psz_pos; playlist_t *p_playlist;
char *psz_media; playlist_item_t *p_item, *p_child;
if( psz_mfield ) char *psz_value;
{ sap_announce_t *p_sap = (sap_announce_t *)malloc(
psz_pos = psz_mfield; sizeof(sap_announce_t ) );
psz_media = psz_mfield; if( !p_sap )
while( *psz_pos != '\0' && *psz_pos != ' ' )
{ {
psz_pos++; msg_Err( p_intf, "out of memory");
p_intf->b_die = VLC_TRUE;
return NULL;
} }
if( *psz_pos != '\0' ) p_sap->i_last = mdate();
{ p_sap->i_hash = i_hash;
*psz_pos = '\0'; p_sap->p_sdp = p_sdp;
if( strcmp( psz_media, "video" ) && strcmp( psz_media, "audio" ) ) p_sap->p_item = NULL;
/* Create the playlist item here */
p_item = playlist_ItemNew( p_intf, p_sap->p_sdp->psz_uri,
p_sap->p_sdp->psz_sessionname );
if( !p_item )
{ {
*ppsz_proto = NULL; return NULL;
*ppsz_port = NULL;
return;
}
} }
psz_pos++;
*ppsz_port = psz_pos; psz_value = GetAttribute( p_sap->p_sdp, "x-plgroup" );
while( *psz_pos != '\0' && *psz_pos !=' ' && *psz_pos!='/' )
if( psz_value == NULL )
{ {
psz_pos++; psz_value = GetAttribute( p_sap->p_sdp, "plgroup" );
} }
if( *psz_pos == '/' ) // FIXME does not support multi-port
{
*psz_pos = '\0';
psz_pos++;
while( *psz_pos != '\0' && *psz_pos !=' ' )
{
psz_pos++;
}
}
*psz_pos = '\0';
psz_pos++;
*ppsz_proto = psz_pos;
while( *psz_pos!='\0' && *psz_pos !=' ' &&
*psz_pos!='/' )
{
*psz_pos = tolower( *psz_pos );
psz_pos++;
}
*psz_pos = '\0';
}
else
{
*ppsz_proto = NULL;
*ppsz_port = NULL;
}
return;
}
/*******************************************************************
* sess_toitem : changes a sess_descr_t into a hurd of
* playlist_item_t, which are enqueued.
*******************************************************************
* Note : does not support sessions that take place on consecutive
* port or adresses yet.
*******************************************************************/
static void sess_toitem( intf_thread_t * p_intf, sess_descr_t * p_sd )
{
struct sap_announce_t *p_announce;
char *psz_uri, *psz_proto, *psz_item_uri;
char *psz_port;
char *psz_uri_default;
int i_count, i, i_id = 0;
vlc_bool_t b_http = VLC_FALSE;
char *psz_http_path = NULL;
playlist_t *p_playlist = NULL;
playlist_item_t *p_item;
psz_uri_default = NULL; p_playlist = (playlist_t *)vlc_object_find( p_intf, VLC_OBJECT_PLAYLIST,
if( p_sd->i_media > 1 || !config_GetInt( p_intf, "sap-parse" ) )
{
asprintf( &psz_uri, "sdp://%s", p_sd->psz_sdp );
/* Check if we have already added the item */
for( i = 0 ; i< p_intf->p_sys->i_announces ; i++ )
{
if( !strcmp( p_intf->p_sys->pp_announces[i]->psz_uri,
psz_uri ) )
{
p_intf->p_sys->pp_announces[i]->i_last = mdate();
free( psz_uri );
return;
}
}
/* Add it to the playlist */
p_playlist = vlc_object_find( p_intf, VLC_OBJECT_PLAYLIST,
FIND_ANYWHERE ); FIND_ANYWHERE );
i_id = playlist_Add( p_playlist, psz_uri, p_sd->psz_sessionname , if( !p_playlist )
PLAYLIST_CHECK_INSERT, PLAYLIST_END );
if( i_id != -1 )
{ {
playlist_item_t *p_item = playlist_ItemGetById( p_playlist, i_id ); msg_Err( p_intf, "playlist not found" );
playlist_ItemSetGroup( p_item, p_intf->p_sys->i_group ); /* FIXME : Free */
return NULL;
} }
/* Remember it */ if( psz_value != NULL )
p_announce = ( struct sap_announce_t * )malloc(
sizeof( struct sap_announce_t ) );
if( p_sd->psz_sessionname )
{ {
p_announce->psz_name = strdup( p_sd->psz_sessionname ); p_child = playlist_ChildSearchName( p_intf->p_sys->p_node, psz_value );
}
else if( p_child == NULL )
{ {
p_announce->psz_name = strdup( "" ); p_child = playlist_NodeCreate( p_playlist, VIEW_CATEGORY,
psz_value, p_intf->p_sys->p_node );
} }
if( psz_uri )
{
p_announce->psz_uri = strdup( psz_uri );
} }
else else
{ {
p_announce->psz_uri = strdup( "" ); p_child = p_intf->p_sys->p_node;
} }
p_announce->i_id = i_id;
p_announce->i_last = mdate();
INSERT_ELEM( p_intf->p_sys->pp_announces, p_item->i_flags &= ~PLAYLIST_SKIP_FLAG;
p_intf->p_sys->i_announces,
p_intf->p_sys->i_announces, playlist_NodeAddItem( p_playlist, p_item, VIEW_CATEGORY, p_child,
p_announce ); PLAYLIST_APPEND, PLAYLIST_END );
vlc_object_release( p_playlist ); vlc_object_release( p_playlist );
free( psz_uri );
return;
}
cfield_parse( p_sd->psz_connection, &psz_uri_default ); p_sap->p_item = p_item;
for( i_count = 0 ; i_count < p_sd->i_media ; i_count++ ) TAB_APPEND( p_intf->p_sys->i_announces,
{ p_intf->p_sys->pp_announces,
int i_group = p_intf->p_sys->i_group; p_sap );
int i_packetsize = config_GetInt( p_intf, "mtu" );
return p_sap;
}
/* Build what we have to put in psz_item_uri, with the m and static char *GetAttribute( sdp_t *p_sdp, const char *psz_search )
* c fields */ {
int i;
if( !p_sd->pp_media[i_count] ) for( i = 0 ; i< p_sdp->i_attributes; i++ )
{
if( !strncmp( p_sdp->pp_attributes[i]->psz_field, psz_search,
strlen( p_sdp->pp_attributes[i]->psz_field ) ) )
{ {
return; return p_sdp->pp_attributes[i]->psz_value;
} }
}
return NULL;
}
mfield_parse( p_sd->pp_media[i_count]->psz_medianame, /* Fill p_sdp->psz_uri */
& psz_proto, & psz_port ); static int ParseConnection( vlc_object_t *p_obj, sdp_t *p_sdp )
{
char *psz_eof;
char *psz_parse;
char *psz_uri = NULL;
char *psz_proto = NULL;
int i_port = 0;
if( !psz_proto || !psz_port ) /* Parse c= field */
if( p_sdp->psz_connection )
{ {
return; psz_parse = p_sdp->psz_connection;
}
psz_eof = strchr( psz_parse, ' ' );
if( p_sd->pp_media[i_count]->psz_mediaconnection ) if( psz_eof )
{ {
cfield_parse( p_sd->pp_media[i_count]->psz_mediaconnection, *psz_eof = '\0';
& psz_uri ); psz_parse = psz_eof + 1;
} }
else else
{ {
psz_uri = psz_uri_default; msg_Warn( p_obj, "unable to parse c field (1)");
return VLC_EGENERIC;
} }
if( psz_uri == NULL ) psz_eof = strchr( psz_parse, ' ' );
{
return;
}
for( i = 0 ; i< p_sd->i_attributes ; i++ ) if( psz_eof )
{
if( !strcasecmp( p_sd->pp_attributes[i]->psz_field , "type" ) &&
strstr( p_sd->pp_attributes[i]->psz_value, "http" ) )
{
b_http = VLC_TRUE;
}
if( !strcasecmp( p_sd->pp_attributes[i]->psz_field , "http-path" ) )
{ {
psz_http_path = strdup( p_sd->pp_attributes[i]->psz_value ); *psz_eof = '\0';
} if( !strncmp( psz_parse, "IP4", 3 ) )
if( ( !strcasecmp( p_sd->pp_attributes[i]->psz_field , "plgroup" ) ) ||
( !strcasecmp( p_sd->pp_attributes[i]->psz_field , "x-plgroup" ) ) )
{ {
int i_group_id; p_sdp->i_in = 4;
p_playlist =
(playlist_t *)vlc_object_find( p_intf, VLC_OBJECT_PLAYLIST,
FIND_ANYWHERE );
if( p_playlist == NULL )
{
return;
} }
else if( !strncmp( psz_parse, "IP6", 3 ) )
i_group_id = playlist_GroupToId( p_playlist,
p_sd->pp_attributes[i]->psz_value );
if( i_group_id != 0 )
{ {
i_group = i_group_id; p_sdp->i_in = 6;
} }
else else
{ {
playlist_group_t *p_group = p_sdp->i_in = 0;
playlist_CreateGroup( p_playlist,
p_sd->pp_attributes[i]->psz_value );
i_group = p_group->i_id;
} }
vlc_object_release( p_playlist ); psz_parse = psz_eof + 1;
} }
if( !strcasecmp( p_sd->pp_attributes[i]->psz_field , "packetsize" ) ) else
{ {
i_packetsize = strtol( p_sd->pp_attributes[i]->psz_value, NULL, 10 ); msg_Warn( p_obj, "unable to parse c field (2)");
} return VLC_EGENERIC;
} }
/* Filling psz_uri */ psz_eof = strchr( psz_parse, '/' );
if( b_http == VLC_FALSE )
{ if( psz_eof )
if( ismult( psz_uri ) )
{ {
asprintf( &psz_item_uri, "%s://@%s:%s", *psz_eof = 0;
psz_proto, psz_uri, psz_port ); psz_uri = strdup( psz_parse );
} }
else else
{ {
asprintf( &psz_item_uri, "%s://%s:%s", msg_Warn( p_obj, "unable to parse c field (3)");
psz_proto, psz_uri, psz_port ); return VLC_EGENERIC;
} }
} }
else
{ /* Parse m= field */
if( psz_http_path == NULL ) if( p_sdp->psz_media )
{ {
psz_http_path = strdup( "/" ); psz_parse = p_sdp->psz_media;
}
if( *psz_http_path == '/' ) psz_eof = strchr( psz_parse, ' ' );
if( psz_eof )
{ {
asprintf( &psz_item_uri, "%s://%s:%s%s", psz_proto, *psz_eof = '\0';
psz_uri, psz_port,psz_http_path );
} if( strncmp( psz_parse, "audio", 5 ) &&
else strncmp( psz_parse, "video",5 ) )
{ {
asprintf( &psz_item_uri, "%s://%s:%s/%s", psz_proto, psz_uri, msg_Warn( p_obj, "unhandled media type -%s-", psz_parse );
psz_port, psz_http_path ); return VLC_EGENERIC;
} }
if( psz_http_path ) psz_parse = psz_eof + 1;
{
free( psz_http_path );
} }
else
{
msg_Warn( p_obj, "unable to parse m field (1)");
return VLC_EGENERIC;
} }
/* Check if we already know this item */ psz_eof = strchr( psz_parse, ' ' );
for( i = 0 ; i< p_intf->p_sys->i_announces ; i++ )
{
if( !strcmp( p_intf->p_sys->pp_announces[i]->psz_uri,
psz_item_uri ) )
{
p_intf->p_sys->pp_announces[i]->i_last = mdate();
/* Check if the name changed */ if( psz_eof )
if( strcmp( p_intf->p_sys->pp_announces[i]->psz_name,
p_sd->psz_sessionname ) )
{ {
playlist_item_t *p_item; *psz_eof = '\0';
p_playlist = vlc_object_find( p_intf, VLC_OBJECT_PLAYLIST,
FIND_ANYWHERE );
msg_Dbg(p_intf, "Name changed (%s -> %s) for %s", /* FIXME : multiple port ! */
p_intf->p_sys->pp_announces[i]->psz_name, i_port = atoi( psz_parse );
p_sd->psz_sessionname,
psz_item_uri );
p_item = playlist_ItemGetById( p_playlist,
p_intf->p_sys->pp_announces[i]->i_id );
/* Change the name in the item */ if( i_port <= 0 || i_port >= 65536 )
if( p_item )
{ {
vlc_mutex_lock( &p_item->input.lock ); msg_Warn( p_obj, "invalid transport port %i", i_port );
if( p_item->input.psz_name )
free( p_item->input.psz_name );
p_item->input.psz_name = strdup( p_sd->psz_sessionname );
vlc_mutex_unlock( &p_item->input.lock );
} }
/* Update the stored name */ psz_parse = psz_eof + 1;
if( p_intf->p_sys->pp_announces[i]->psz_name )
free( p_intf->p_sys->pp_announces[i]->psz_name );
p_intf->p_sys->pp_announces[i]->psz_name =
strdup( p_sd->psz_sessionname );
vlc_object_release( p_playlist );
}
free( psz_item_uri );
return;
} }
else
{
msg_Warn( p_obj, "unable to parse m field (2)");
return VLC_EGENERIC;
} }
/* Add the item in the playlist */ psz_eof = strchr( psz_parse, ' ' );
p_playlist = vlc_object_find( p_intf, VLC_OBJECT_PLAYLIST,
FIND_ANYWHERE );
p_item = playlist_ItemNew( p_intf, psz_item_uri, p_sd->psz_sessionname ); if( psz_eof )
if( p_item )
{ {
playlist_ItemSetGroup( p_item, i_group ); *psz_eof = '\0';
if( i_packetsize > config_GetInt( p_intf, "mtu" ) )
psz_proto = strdup( psz_parse );
}
else
{ {
char *psz_packetsize_option; msg_Warn( p_obj, "unable to parse m field (3)");
asprintf( &psz_packetsize_option, "mtu=%i", i_packetsize ); return VLC_EGENERIC;
playlist_ItemAddOption( p_item, psz_packetsize_option );
free( psz_packetsize_option );
} }
playlist_AddItem( p_playlist , p_item , PLAYLIST_CHECK_INSERT, PLAYLIST_END );
} }
/* Then remember it */ /* FIXME: HTTP support */
p_announce = (struct sap_announce_t *)malloc(
sizeof( struct sap_announce_t ) ); if( i_port == 0 )
if( p_sd->psz_sessionname )
{
p_announce->psz_name = strdup( p_sd->psz_sessionname );
}
else
{ {
p_announce->psz_name = strdup( "" ); i_port = 1234;
} }
if( psz_item_uri )
if( ismult( psz_uri ) )
{ {
p_announce->psz_uri = strdup( psz_item_uri ); asprintf( &p_sdp->psz_uri, "%s://@%s:%i", psz_proto, psz_uri, i_port );
} }
else else
{ {
p_announce->psz_uri = strdup( "" ); asprintf( &p_sdp->psz_uri, "%s://%s:%i", psz_proto, psz_uri, i_port );
}
p_announce->i_id = i_id;
p_announce->i_last = mdate();
vlc_object_release( p_playlist );
INSERT_ELEM( p_intf->p_sys->pp_announces,
p_intf->p_sys->i_announces,
p_intf->p_sys->i_announces,
p_announce );
free( psz_item_uri );
} }
return VLC_SUCCESS;
} }
/*********************************************************************** /***********************************************************************
* parse_sdp : SDP parsing * ParseSDP : SDP parsing
* ********************************************************************* * *********************************************************************
* Make a sess_descr_t with a psz * Validate SDP and parse all fields
***********************************************************************/ ***********************************************************************/
static sdp_t * ParseSDP( vlc_object_t *p_obj, char* psz_sdp )
static sess_descr_t * parse_sdp( vlc_object_t * p_parent, char *p_packet )
{ {
sess_descr_t * sd; sdp_t *p_sdp;
if( p_packet[0] != 'v' || p_packet[1] != '=' ) if( psz_sdp[0] != 'v' || psz_sdp[1] != '=' )
{ {
msg_Warn( p_parent, "bad SDP packet" ); msg_Warn( p_obj, "bad SDP packet" );
return NULL; return NULL;
} }
sd = malloc( sizeof( sess_descr_t ) ); p_sdp = (sdp_t *)malloc( sizeof( sdp_t ) );
sd->psz_sessionname = NULL;
sd->psz_connection = NULL;
sd->psz_sdp = strdup( p_packet );
sd->i_media = 0; p_sdp->psz_sdp = strdup( psz_sdp );
sd->pp_media = NULL;
sd->i_attributes = 0;
sd->pp_attributes = NULL;
while( *p_packet != '\0' ) p_sdp->psz_sessionname = NULL;
{ p_sdp->psz_media = NULL;
char *psz_end; p_sdp->psz_connection = NULL;
p_sdp->i_media = 0;
p_sdp->i_attributes = 0;
p_sdp->pp_attributes = NULL;
/* Search begin of field */ while( *psz_sdp != '\0' )
while( *p_packet == '\r' || *p_packet == '\n' || *p_packet == ' ' || *p_packet == '\t' ) {
char *psz_eol;
while( *psz_sdp == '\r' || *psz_sdp == '\n' || *psz_sdp == ' ' || *psz_sdp == '\t' )
{ {
p_packet++; psz_sdp++;
} }
/* search end of line */
if( ( psz_end = strchr( p_packet, '\n' ) ) == NULL ) if( ( psz_eol = strchr( psz_sdp, '\n' ) ) == NULL )
{ {
psz_end = p_packet + strlen( p_packet ); psz_eol = psz_sdp + strlen( psz_sdp );
} }
if( psz_end > p_packet && *( psz_end - 1 ) == '\r' ) if( psz_eol > psz_sdp && *( psz_eol - 1 ) == '\r' )
{ {
psz_end--; psz_eol--;
} }
if( psz_end <= p_packet ) if( psz_eol <= psz_sdp )
{ {
break; break;
} }
*psz_end++ = '\0'; *psz_eol++ = '\0';
if( p_packet[1] != '=' ) /* no space allowed between fields */
if( psz_sdp[1] != '=' )
{ {
msg_Warn( p_parent, "invalid packet" ) ; msg_Warn( p_obj, "invalid packet" ) ;
free_sd( sd ); /* MEMLEAK ! */
return NULL; return NULL;
} }
switch( p_packet[0] ) /* Now parse each line */
switch( psz_sdp[0] )
{ {
case( 'v' ): case( 'v' ):
sd->i_version = atoi( &p_packet[2] );
break; break;
case( 's' ): case( 's' ):
sd->psz_sessionname = strdup( &p_packet[2] ); p_sdp->psz_sessionname = strdup( &psz_sdp[2] );
break; break;
case ( 'o' ): case ( 'o' ):
case( 'i' ): case( 'i' ):
...@@ -1029,87 +972,69 @@ static sess_descr_t * parse_sdp( vlc_object_t * p_parent, char *p_packet ) ...@@ -1029,87 +972,69 @@ static sess_descr_t * parse_sdp( vlc_object_t * p_parent, char *p_packet )
case( 't' ): case( 't' ):
case( 'r' ): case( 'r' ):
break; break;
case( 'a' ): case( 'a' ): /* attribute */
{
char *psz_eof = strchr( &p_packet[2], ':' );
if( psz_eof && psz_eof[1] != '\0' )
{ {
attr_descr_t *attr = malloc( sizeof( attr_descr_t ) ); char *psz_eon = strchr( &psz_sdp[2], ':' );
*psz_eof++ = '\0'; attribute_t *p_attr = (attribute_t *)malloc(
sizeof( attribute_t ) );
attr->psz_field = strdup( &p_packet[2] ); /* Attribute with value */
attr->psz_value = strdup( psz_eof ); if( psz_eon )
{
*psz_eon++ = '\0';
TAB_APPEND( sd->i_attributes, sd->pp_attributes, attr ); p_attr->psz_field = strdup( &psz_sdp[2] );
p_attr->psz_value = strdup( psz_eon );
} }
else /* Attribute without value */
{
p_attr->psz_field = strdup( &psz_sdp[2] );
p_attr->psz_value = NULL;
}
TAB_APPEND( p_sdp->i_attributes, p_sdp->pp_attributes, p_attr );
break; break;
} }
case( 'm' ): case( 'm' ): /* Media announcement */
{ {
media_descr_t *media = malloc( sizeof( media_descr_t ) ); /* If we have several medias, we pass the announcement to
* LIVE.COM, so just count them */
media->psz_medianame = strdup( &p_packet[2] ); p_sdp->i_media++;
media->psz_mediaconnection = NULL; if( p_sdp->i_media == 1 )
{
TAB_APPEND( sd->i_media, sd->pp_media, media ); p_sdp->psz_media = strdup( &psz_sdp[2] );
}
break; break;
} }
case( 'c' ): case( 'c' ):
if( sd->i_media <= 0 )
{ {
sd->psz_connection = strdup( &p_packet[2] ); if( p_sdp->i_media > 1 )
} break;
else
{ p_sdp->psz_connection = strdup( &psz_sdp[2] );
sd->pp_media[sd->i_media-1]->psz_mediaconnection = strdup( &p_packet[2] );
}
break; break;
}
default: default:
break; break;
} }
p_packet = psz_end; psz_sdp = psz_eol;
} }
return sd; return p_sdp;
} }
#define FREE( p ) \ #define FREE( p ) \
if( p ) { free( p ); (p) = NULL; } if( p ) { free( p ); (p) = NULL; }
static void free_sd( sess_descr_t * p_sd )
{
int i;
FREE( p_sd->psz_sessionname );
FREE( p_sd->psz_connection );
FREE( p_sd->psz_sdp );
for( i = 0; i < p_sd->i_media ; i++ )
{
FREE( p_sd->pp_media[i]->psz_medianame );
FREE( p_sd->pp_media[i]->psz_mediaconnection );
FREE( p_sd->pp_media[i] );
}
for( i = 0; i < p_sd->i_attributes ; i++ )
{
FREE( p_sd->pp_attributes[i]->psz_field );
FREE( p_sd->pp_attributes[i]->psz_value );
FREE( p_sd->pp_attributes[i] );
}
FREE( p_sd->pp_attributes );
FREE( p_sd->pp_media );
free( p_sd );
}
/*********************************************************************** /***********************************************************************
* ismult: returns true if we have a multicast address * ismult: returns true if we have a multicast address
***********************************************************************/ ***********************************************************************/
static int ismult( char *psz_uri ) static int ismult( char *psz_uri )
{ {
char *psz_end; char *psz_end;
...@@ -1132,113 +1057,40 @@ static int ismult( char *psz_uri ) ...@@ -1132,113 +1057,40 @@ static int ismult( char *psz_uri )
return( i_value < 224 ? VLC_FALSE : VLC_TRUE ); return( i_value < 224 ? VLC_FALSE : VLC_TRUE );
} }
/***************************************************************************** static int InitSocket( intf_thread_t *p_intf, char *psz_address, int i_port )
* NetRead: read on a file descriptor, checking b_die periodically
*****************************************************************************
* Taken from net.c
* Code duplication because of select(). We need a net_Select() but that's
* quite difficult.
*****************************************************************************/
static ssize_t NetRead( intf_thread_t *p_intf,
int fd[2], uint8_t *p_buffer, int i_len )
{ {
#ifdef UNDER_CE int i_fd = net_OpenUDP( p_intf, psz_address, i_port, "", 0 );
return -1;
#else
struct timeval timeout;
fd_set fds;
int i_ret;
int i_handle_max = __MAX( fd[0], fd[1] );
/* Initialize file descriptor set */
FD_ZERO( &fds );
if( fd[0] > 0 ) FD_SET( fd[0], &fds );
if( fd[1] > 0 ) FD_SET( fd[1], &fds );
/* We'll wait 0.5 second if nothing happens */ if( i_fd != -1 )
timeout.tv_sec = 0;
timeout.tv_usec = 500000;
/* Find if some data is available */
i_ret = select( i_handle_max + 1, &fds, NULL, NULL, &timeout );
if( i_ret == -1 && errno != EINTR )
{
msg_Err( p_intf, "network select error (%s)", strerror(errno) );
}
else if( i_ret > 0 )
{
if( fd[0] > 0 && FD_ISSET( fd[0], &fds ) )
{ {
return recv( fd[0], p_buffer, i_len, 0 ); INSERT_ELEM( p_intf->p_sys->pi_fd,
} p_intf->p_sys->i_fd,
else if( fd[1] > 0 && FD_ISSET( fd[1], &fds ) ) p_intf->p_sys->i_fd,
{ i_fd );
return recv( fd[1], p_buffer, i_len, 0 ); return VLC_SUCCESS;
}
} }
return 0;
#endif
}
/*
* This submodule is temporary and has a very high duplicate code rate
* This needs to be removed when SDP UDP parsing is fixed in liveMedia
*/
static int OpenSDP( vlc_object_t * p_this )
{
demux_t *p_demux = (demux_t *)p_this;
uint8_t *p_peek;
if( p_demux->s )
{
/* See if it looks like a SDP
v, o, s fields are mandatory and in this order */
if( stream_Peek( p_demux->s, &p_peek, 7 ) < 7 )
{
msg_Err( p_demux, "cannot peek" );
return VLC_EGENERIC;
}
if( strncmp( (char*)p_peek, "v=0\r\n", 5 ) && strncmp( (char*)p_peek, "v=0\n", 4 ) &&
( p_peek[0] < 'a' || p_peek[0] > 'z' || p_peek[1] != '=' ) )
{
msg_Warn( p_demux, "SDP (UDP) module discarded" );
return VLC_EGENERIC; return VLC_EGENERIC;
}
}
p_demux->pf_control = Control;
p_demux->pf_demux = Demux;
return VLC_SUCCESS;
} }
/***************************************************************************** #ifdef HAVE_ZLIB_H
* Demux: reads and demuxes data packets static int Decompress( unsigned char *psz_src, unsigned char **_dst, int i_len )
* This submodule is temporary and has a very high duplicate code rate
* This needs to be removed when SDP UDP parsing is fixed in liveMedia
*****************************************************************************
* Returns -1 in case of error, 0 in case of EOF, 1 otherwise
*****************************************************************************/
static int Demux( demux_t *p_demux )
{ {
sess_descr_t *p_sd = NULL; int i_result, i_dstsize, n;
playlist_t *p_playlist = NULL; unsigned char *psz_dst;
int i_sdp = 0; z_stream d_stream;
int i_sdp_max = 1000;
int i_position = -1;
char *p_sdp = (uint8_t*)malloc( i_sdp_max );
p_playlist = vlc_object_find( p_demux, VLC_OBJECT_PLAYLIST, d_stream.zalloc = (alloc_func)0;
FIND_ANYWHERE ); d_stream.zfree = (free_func)0;
d_stream.opaque = (voidpf)0;
if( !p_playlist ) i_result = inflateInit(&d_stream);
if( i_result != Z_OK )
{ {
msg_Err( p_demux, "can't find playlist" ); printf( "inflateInit() failed. Result: %d\n", i_result );
return -1; return( -1 );
} }
#if 0
p_playlist->pp_items[p_playlist->i_index]->b_autodeletion = VLC_TRUE; p_playlist->pp_items[p_playlist->i_index]->b_autodeletion = VLC_TRUE;
i_position = p_playlist->i_index; i_position = p_playlist->i_index;
...@@ -1246,195 +1098,51 @@ static int Demux( demux_t *p_demux ) ...@@ -1246,195 +1098,51 @@ static int Demux( demux_t *p_demux )
for( ;; ) for( ;; )
{ {
int i_read = stream_Read( p_demux->s, &p_sdp[i_sdp], i_sdp_max - i_sdp - 1 ); int i_read = stream_Read( p_demux->s, &p_sdp[i_sdp], i_sdp_max - i_sdp - 1 );
#endif
d_stream.next_in = (Bytef *)psz_src;
d_stream.avail_in = i_len;
n = 0;
if( i_read < 0 ) psz_dst = NULL;
{
msg_Err( p_demux, "failed to read SDP" );
return VLC_EGENERIC;
}
i_sdp += i_read;
if( i_read < i_sdp_max - i_sdp - 1 )
{
p_sdp[i_sdp] = '\0';
break;
}
i_sdp_max += 1000;
p_sdp = (uint8_t*)realloc( p_sdp, i_sdp_max );
}
msg_Dbg( p_demux, "sdp=%s\n", p_sdp );
p_sd = parse_sdp( (vlc_object_t *)p_demux, p_sdp );
if( p_sd )
{
char *psz_uri, *psz_proto, *psz_item_uri;
char *psz_port;
char *psz_uri_default;
int i_count, i, i_id = 0;
vlc_bool_t b_http = VLC_FALSE;
char *psz_http_path = NULL;
playlist_item_t *p_item;
psz_uri_default = NULL;
if( p_sd->i_media > 1 || !config_GetInt( p_demux, "sap-parse" ) )
{
/* Let another module try this */
asprintf( &psz_uri, "sdp://%s", p_sd->psz_sdp );
i_id = playlist_Add( p_playlist, psz_uri, p_sd->psz_sessionname ,
PLAYLIST_CHECK_INSERT, i_position );
free( psz_uri );
vlc_object_release( p_playlist );
return 0;
}
/* We try to parse it ourselves */
cfield_parse( p_sd->psz_connection, &psz_uri_default );
for( i_count = 0 ; i_count < p_sd->i_media ; i_count++ )
{
int i_group = 0;
int i_packetsize = config_GetInt( p_demux, "mtu" );
/* Build what we have to put in psz_item_uri, with the m and
* c fields */
if( !p_sd->pp_media[i_count] )
{
vlc_object_release( p_playlist );
return -1;
}
mfield_parse( p_sd->pp_media[i_count]->psz_medianame,
&psz_proto, &psz_port );
if( !psz_proto || !psz_port )
{
vlc_object_release( p_playlist );
return -1;
}
if( p_sd->pp_media[i_count]->psz_mediaconnection )
{
cfield_parse( p_sd->pp_media[i_count]->psz_mediaconnection,
& psz_uri );
}
else
{
psz_uri = psz_uri_default;
}
if( psz_uri == NULL )
{
vlc_object_release( p_playlist );
return -1;
}
for( i = 0 ; i< p_sd->i_attributes ; i++ )
{
if( !strcasecmp( p_sd->pp_attributes[i]->psz_field , "type" ) &&
strstr( p_sd->pp_attributes[i]->psz_value, "http" ) )
{
b_http = VLC_TRUE;
}
if( !strcasecmp( p_sd->pp_attributes[i]->psz_field , "http-path" ) )
{
psz_http_path = strdup( p_sd->pp_attributes[i]->psz_value );
}
if( ( !strcasecmp( p_sd->pp_attributes[i]->psz_field , "plgroup" ) ) ||
( !strcasecmp( p_sd->pp_attributes[i]->psz_field , "x-plgroup" ) ) )
{
int i_group_id;
i_group_id = playlist_GroupToId( p_playlist,
p_sd->pp_attributes[i]->psz_value );
if( i_group_id != 0 )
{
i_group = i_group_id;
}
else
{
playlist_group_t *p_group =
playlist_CreateGroup( p_playlist,
p_sd->pp_attributes[i]->psz_value );
i_group = p_group->i_id;
}
}
if( !strcasecmp( p_sd->pp_attributes[i]->psz_field , "packetsize" ) )
{
i_packetsize = strtol( p_sd->pp_attributes[i]->psz_value, NULL, 10 );
}
}
/* Filling psz_uri */ do
if( b_http == VLC_FALSE )
{
if( ismult( psz_uri ) )
{
asprintf( &psz_item_uri, "%s://@%s:%s",
psz_proto, psz_uri, psz_port );
}
else
{
asprintf( &psz_item_uri, "%s://%s:%s",
psz_proto, psz_uri, psz_port );
}
}
else
{
if( psz_http_path == NULL )
{
psz_http_path = strdup( "/" );
}
if( *psz_http_path == '/' )
{
asprintf( &psz_item_uri, "%s://%s:%s%s", psz_proto,
psz_uri, psz_port,psz_http_path );
}
else
{ {
asprintf( &psz_item_uri, "%s://%s:%s/%s", psz_proto, psz_uri, n++;
psz_port, psz_http_path ); psz_dst = (unsigned char *)realloc( psz_dst, n * 1000 );
} d_stream.next_out = (Bytef *)&psz_dst[(n - 1) * 1000];
d_stream.avail_out = 1000;
if( psz_http_path ) i_result = inflate(&d_stream, Z_NO_FLUSH);
if( ( i_result != Z_OK ) && ( i_result != Z_STREAM_END ) )
{ {
free( psz_http_path ); printf( "Zlib decompression failed. Result: %d\n", i_result );
return( -1 );
} }
} }
while( ( d_stream.avail_out == 0 ) && ( d_stream.avail_in != 0 ) &&
( i_result != Z_STREAM_END ) );
/* Add the item in the playlist */ i_dstsize = d_stream.total_out;
p_item = playlist_ItemNew( p_demux, psz_item_uri, p_sd->psz_sessionname ); inflateEnd( &d_stream );
if( p_item )
{
playlist_ItemSetGroup( p_item, i_group );
if( i_packetsize > config_GetInt( p_demux, "mtu" ) )
{
char *psz_packetsize_option;
asprintf( &psz_packetsize_option, "mtu=%i", i_packetsize );
playlist_ItemAddOption( p_item, psz_packetsize_option );
free( psz_packetsize_option );
}
playlist_AddItem( p_playlist , p_item , PLAYLIST_CHECK_INSERT, i_position );
}
free( psz_item_uri ); *_dst = (unsigned char *)realloc( psz_dst, i_dstsize );
}
free_sd ( p_sd ); return i_dstsize;
}
vlc_object_release( p_playlist );
return 0;
} }
#endif
static int Control( demux_t *p_demux, int i_query, va_list args ) static int RemoveAnnounce( intf_thread_t *p_intf, sap_announce_t *p_announce )
{ {
return VLC_EGENERIC; msg_Err( p_intf, "remove not implemented");
return VLC_SUCCESS;
} }
static void CloseSDP( vlc_object_t *p_this )
static void CacheLoad( intf_thread_t *p_intf )
{
msg_Warn( p_intf, "Cache not implemented") ;
}
static void CacheSave( intf_thread_t *p_intf )
{ {
msg_Warn( p_intf, "Cache not implemented") ;
} }
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment