Update of /cvsroot/mplayer/main In directory usw-pr-cvs1:/tmp/cvs-serv18124 Modified Files: network.c network.h Log Message: Continue implementation of the network streaming part. Index: network.c =================================================================== RCS file: /cvsroot/mplayer/main/network.c,v retrieving revision 1.3 retrieving revision 1.4 diff -C2 -r1.3 -r1.4 *** network.c 2001/05/29 17:03:17 1.3 --- network.c 2001/06/04 17:52:33 1.4 *************** *** 5,15 **** */ #include <unistd.h> ! #include <netinet/in.h> #include <netdb.h> #include <sys/types.h> #include <sys/socket.h> #include <arpa/inet.h> #include <ctype.h> #include <string.h> --- 5,22 ---- */ + #define DUMP2FILE + #include <unistd.h> ! #include <fcntl.h> #include <netdb.h> + #include <netinet/in.h> + #include <sys/time.h> #include <sys/types.h> #include <sys/socket.h> #include <arpa/inet.h> + #include <pthread.h> + + #include <errno.h> #include <ctype.h> #include <string.h> *************** *** 17,20 **** --- 24,30 ---- #include <stdio.h> + #include "stream.h" + #include "demuxer.h" + #include "network.h" #include "http.h" *************** *** 22,32 **** --- 32,177 ---- #include "asf.h" + streaming_ctrl_t *streaming_ctrl; + + static ASF_StreamType_e streaming_type = ASF_Unknown_e; + + Net_Fifo * + net_fifo_new() { + Net_Fifo *net_fifo; + net_fifo = (Net_Fifo*)malloc(sizeof(Net_Fifo)); + if( net_fifo==NULL ) { + printf("Memory allocation failed\n"); + return NULL; + } + memset( net_fifo, 0, sizeof(Net_Fifo) ); + return net_fifo; + } + + void + net_fifo_free( Net_Fifo *net_fifo ) { + if( net_fifo->buffer!=NULL ) free( net_fifo->buffer ); + free( net_fifo ); + } + + int + net_fifo_push(Net_Fifo *net_fifo, char *buffer, int length ) { + char *ptr; + if( net_fifo==NULL || buffer==NULL || length<0 ) return -1; + + ptr = (char*)malloc(length+net_fifo->length); + if( ptr==NULL ) { + printf("Memory allocation failed\n"); + return -1; + } + if( net_fifo->buffer!=NULL ) { + memcpy( ptr, net_fifo->buffer, net_fifo->length ); + free( net_fifo->buffer ); + } + memcpy( ptr+net_fifo->length, buffer, length ); + net_fifo->buffer = ptr; + net_fifo->length += length; + return net_fifo->length; + } + + int + net_fifo_pop(Net_Fifo *net_fifo, char *buffer, int length ) { + char *ptr; + int len; + if( net_fifo==NULL || buffer==NULL || length<0 ) return -1; + if( net_fifo->buffer==NULL || net_fifo->length==0 ) return -1; + + len = MIN(net_fifo->length, length); + + ptr = (char*)malloc(net_fifo->length-len); + if( ptr==NULL ) { + printf("Memory allocation failed\n"); + return -1; + } + memcpy( buffer, net_fifo->buffer, len ); + if( net_fifo->length-len!=0 ) { + memcpy( ptr, net_fifo->buffer+len, net_fifo->length-len ); + free( net_fifo->buffer ); + net_fifo->buffer = ptr; + net_fifo->length -= len; + } else { + free( net_fifo->buffer ); + net_fifo->buffer = NULL; + net_fifo->length = 0; + } + return len; + } + + streaming_ctrl_t * + streaming_ctrl_new( ) { + streaming_ctrl_t *streaming_ctrl; + streaming_ctrl = (streaming_ctrl_t*)malloc(sizeof(streaming_ctrl_t)); + if( streaming_ctrl==NULL ) { + printf("Failed to allocate memory\n"); + return NULL; + } + memset( streaming_ctrl, 0, sizeof(streaming_ctrl_t) ); + streaming_ctrl->buffer = net_fifo_new(); + return streaming_ctrl; + } + + void + streaming_ctrl_free( streaming_ctrl_t *streaming_ctrl ) { + if( streaming_ctrl==NULL ) return; + if( streaming_ctrl->buffer!=NULL ) net_fifo_free( streaming_ctrl->buffer ); + free( streaming_ctrl ); + } + + int + readFromServer(int fd, char *buffer, int length) { + int ret; + int done=0; + fd_set set; + struct timeval tv; + if( buffer==NULL || length<0 ) return -1; + + + // fcntl( fd, F_SETFL, fcntl(fd, F_GETFL) & ~O_NONBLOCK ); + return read( fd, buffer, length ); + + do { + tv.tv_sec = 0; + tv.tv_usec = 10000; // 10 milli-seconds timeout + FD_ZERO( &set ); + FD_SET( fd, &set ); + ret = select( fd+1, &set, NULL, NULL, &tv ); + if( ret<0 ) { + perror("select"); + } else if( ret==0 ) { + printf("timeout\n"); + } + if( FD_ISSET(fd, &set) ) { + ret = read( fd, buffer, length ); + if( ret<0 ) { + if( errno!=EINPROGRESS ) { + } + } else { + done = 1; + } + } else { + return -1; + } + } while( !done ); + + return ret; + } + // Connect to a server using a TCP connection int connect2Server(char *host, int port) { int socket_server_fd; + int err, err_len; + fd_set set; + struct timeval tv; struct sockaddr_in server_address; + printf("Connecting to server %s:%d ...\n", host, port ); + socket_server_fd = socket(AF_INET, SOCK_STREAM, 0); + // fcntl( socket_server_fd, F_SETFL, fcntl(socket_server_fd, F_GETFL) | O_NONBLOCK ); if( socket_server_fd==-1 ) { perror("Failed to create socket"); *************** *** 37,41 **** struct hostent *hp =(struct hostent*)gethostbyname( host ); if( hp==NULL ) { ! printf("Unknown host: %s\n", host); return -1; } --- 182,186 ---- struct hostent *hp =(struct hostent*)gethostbyname( host ); if( hp==NULL ) { ! printf("Counldn't resolve name: %s\n", host); return -1; } *************** *** 48,58 **** if( connect( socket_server_fd, (struct sockaddr*)&server_address, sizeof(server_address) )==-1 ) { ! perror("Failed to connect to server"); ! close(socket_server_fd); ! return -1; } return socket_server_fd; } // By using the protocol, the extension of the file or the content-type // we might be able to guess the streaming type. --- 193,264 ---- if( connect( socket_server_fd, (struct sockaddr*)&server_address, sizeof(server_address) )==-1 ) { ! if( errno!=EINPROGRESS ) { ! perror("Failed to connect to server"); ! close(socket_server_fd); ! return -1; ! } } + + tv.tv_sec = 0; + tv.tv_usec = 10000; // 10 milli-seconds timeout + FD_ZERO( &set ); + FD_SET( socket_server_fd, &set ); + if( select(socket_server_fd+1, NULL, &set, NULL, &tv)>0 ) { + err_len = sizeof( err ); + getsockopt( socket_server_fd, SOL_SOCKET, SO_ERROR, &err, &err_len ); + if( err ) { + printf("Couldn't connect to host %s\n", host ); + printf("Socket error: %d\n", err ); + close(socket_server_fd); + return -1; + } + } return socket_server_fd; } + int + http_send_request( URL_t *url ) { + HTTP_header_t *http_hdr; + int fd; + http_hdr = http_new_header(); + http_set_uri( http_hdr, url->file ); + http_set_field( http_hdr, "User-Agent: MPlayer"); + http_set_field( http_hdr, "Connection: closed"); + if( http_build_request( http_hdr )==NULL ) { + return -1; + } + + fd = connect2Server( url->hostname, url->port ); + if( fd<0 ) { + return -1; + } + write( fd, http_hdr->buffer, http_hdr->buffer_size ); + http_free( http_hdr ); + + return fd; + } + + HTTP_header_t * + http_read_response( int fd ) { + HTTP_header_t *http_hdr; + char response[BUFFER_SIZE]; + int i; + + http_hdr = http_new_header(); + if( http_hdr==NULL ) { + return NULL; + } + + do { + i = readFromServer( fd, response, BUFFER_SIZE ); + if( i<0 ) { + printf("Read failed\n"); + } + http_response_append( http_hdr, response, i ); + } while( !http_is_header_entired( http_hdr ) ); + http_response_parse( http_hdr ); + return http_hdr; + } + // By using the protocol, the extension of the file or the content-type // we might be able to guess the streaming type. *************** *** 62,65 **** --- 268,272 ---- int fd=-1; int i; + int redirect; char *extension; char *content_type; *************** *** 67,226 **** char response[1024]; ! redo_request: ! *fd_out=-1; ! next_url = NULL; ! extension = NULL; ! content_type = NULL; ! ! if( url==NULL ) return STREAMING_TYPE_UNKNOWN; ! ! // Get the extension of the file if present ! if( url->file!=NULL ) { ! for( i=strlen(url->file) ; i>0 ; i-- ) { ! if( url->file[i]=='.' ) { ! extension=(url->file)+i+1; ! break; } } ! } ! ! if( extension!=NULL ) { ! printf("Extension: %s\n", extension ); ! if( !strcasecmp(extension, "asf") || ! !strcasecmp(extension, "wmv") || ! !strcasecmp(extension, "asx") ) { if( url->port==0 ) url->port = 80; ! return STREAMING_TYPE_ASF; } - } ! // Checking for RTSP ! if( !strcasecmp(url->protocol, "rtsp") ) { ! printf("RTSP protocol not yet implemented!\n"); ! return STREAMING_TYPE_UNKNOWN; ! } ! // Checking for ASF ! if( !strcasecmp(url->protocol, "mms") ) { ! if( url->port==0 ) url->port = 80; ! return STREAMING_TYPE_ASF; ! } ! // HTTP based protocol ! if( !strcasecmp(url->protocol, "http") ) { ! if( url->port==0 ) url->port = 80; ! http_hdr = http_new_header(); ! http_set_uri( http_hdr, url->file ); ! http_set_field( http_hdr, "User-Agent: MPlayer"); ! http_set_field( http_hdr, "Connection: closed"); ! if( http_build_request( http_hdr )==NULL ) { ! return STREAMING_TYPE_UNKNOWN; ! } ! fd = connect2Server( url->hostname, url->port ); ! if( fd<0 ) { ! *fd_out=-1; ! return STREAMING_TYPE_UNKNOWN; } ! write( fd, http_hdr->buffer, http_hdr->buffer_size ); ! // http_free( http_hdr ); ! http_hdr = http_new_header(); ! if( http_hdr==NULL ) { ! close( fd ); ! *fd_out=-1; ! return STREAMING_TYPE_UNKNOWN; ! } ! do { ! i = read( fd, response, 1024 ); ! http_response_append( http_hdr, response, i ); ! } while( !http_is_header_entired( http_hdr ) ); ! http_response_parse( http_hdr ); ! ! *fd_out=fd; ! //http_debug_hdr( http_hdr ); ! ! // Check if the response is an ICY status_code reason_phrase ! if( !strcasecmp(http_hdr->protocol, "ICY") ) { ! // Ok, we have detected an mp3 streaming ! return STREAMING_TYPE_MP3; ! } switch( http_hdr->status_code ) { case 200: // OK ! // Look if we can use the Content-Type ! content_type = http_get_field( http_hdr, "Content-Type" ); ! if( content_type!=NULL ) { ! printf("Content-Type: %s\n", content_type ); ! // Check for ASF ! if( asf_http_streaming_type(content_type, NULL)!=ASF_Unknown_e ) { ! return STREAMING_TYPE_ASF; ! } ! // Check for MP3 streaming ! // Some MP3 streaming server answer with audio/mpeg ! if( !strcasecmp(content_type, "audio/mpeg") ) { ! return STREAMING_TYPE_MP3; ! } } break; - // Redirect - case 301: // Permanently - case 302: // Temporarily - // RFC 2616, recommand to detect infinite redirection loops - next_url = http_get_field( http_hdr, "Location" ); - if( next_url!=NULL ) { - close( fd ); - url_free( url ); - url_new( next_url ); - goto redo_request; - } - //break; default: ! printf("Server returned %d: %s\n", http_hdr->status_code, http_hdr->reason_phrase ); close( fd ); ! *fd_out=-1; ! return STREAMING_TYPE_UNKNOWN; } } - return STREAMING_TYPE_UNKNOWN; - } void ! network_streaming() { int ret; ! /* do { ! ret = select( ); ! } while( ); ! */ } int streaming_start(URL_t **url, int fd, int streaming_type) { switch( streaming_type ) { ! case STREAMING_TYPE_ASF: ! // Send a the appropriate HTTP request ! if( fd>0 ) close( fd ); ! fd = asf_http_streaming_start( url ); break; ! case STREAMING_TYPE_MP3: ! // Nothing else to do the server is already feeding the pipe. break; ! case STREAMING_TYPE_UNKNOWN: default: printf("Unable to detect the streaming type\n"); close( fd ); return -1; } ! return fd; } int streaming_stop( ) { ! } --- 274,591 ---- char response[1024]; ! do { ! *fd_out=-1; ! next_url = NULL; ! extension = NULL; ! content_type = NULL; ! redirect = 0; ! ! if( url==NULL ) return DEMUXER_TYPE_UNKNOWN; ! ! // Get the extension of the file if present ! if( url->file!=NULL ) { ! for( i=strlen(url->file) ; i>0 ; i-- ) { ! if( url->file[i]=='.' ) { ! extension=(url->file)+i+1; ! break; ! } } } ! // extension=NULL; ! if( extension!=NULL ) { ! printf("Extension: %s\n", extension ); ! if( !strcasecmp(extension, "asf") || ! !strcasecmp(extension, "wmv") || ! !strcasecmp(extension, "asx") ) { ! if( url->port==0 ) url->port = 80; ! return DEMUXER_TYPE_ASF; ! } ! if( !strcasecmp(extension, "mpg") || ! !strcasecmp(extension, "mpeg") ) { ! if( url->port==0 ) url->port = 80; ! return DEMUXER_TYPE_MPEG_PS; ! } ! if( !strcasecmp(extension, "avi") ) { ! if( url->port==0 ) url->port = 80; ! return DEMUXER_TYPE_AVI; ! } ! } ! ! // Checking for RTSP ! if( !strcasecmp(url->protocol, "rtsp") ) { ! printf("RTSP protocol not yet implemented!\n"); ! return DEMUXER_TYPE_UNKNOWN; ! } ! ! // Checking for ASF ! if( !strcasecmp(url->protocol, "mms") ) { if( url->port==0 ) url->port = 80; ! return DEMUXER_TYPE_ASF; } ! // HTTP based protocol ! if( !strcasecmp(url->protocol, "http") ) { ! if( url->port==0 ) url->port = 80; ! fd = http_send_request( url ); ! if( fd<0 ) { ! *fd_out=-1; ! return DEMUXER_TYPE_UNKNOWN; ! } ! http_hdr = http_read_response( fd ); ! if( http_hdr==NULL ) { ! close( fd ); ! *fd_out=-1; ! return DEMUXER_TYPE_UNKNOWN; ! } ! *fd_out=fd; ! //http_debug_hdr( http_hdr ); ! // Check if the response is an ICY status_code reason_phrase ! if( !strcasecmp(http_hdr->protocol, "ICY") ) { ! // Ok, we have detected an mp3 streaming ! return DEMUXER_TYPE_MPEG_PS; ! } ! ! switch( http_hdr->status_code ) { ! case 200: // OK ! // Look if we can use the Content-Type ! content_type = http_get_field( http_hdr, "Content-Type" ); ! if( content_type!=NULL ) { ! printf("Content-Type: [%s]\n", content_type ); ! printf("Content-Length: [%s]\n", http_get_field(http_hdr, "Content-Length") ); ! // Check for ASF ! if( asf_http_streaming_type(content_type, NULL)!=ASF_Unknown_e ) { ! return DEMUXER_TYPE_ASF; ! } ! // Check for MP3 streaming ! // Some MP3 streaming server answer with audio/mpeg ! if( !strcasecmp(content_type, "audio/mpeg") ) { ! return DEMUXER_TYPE_MPEG_PS; ! } ! // Check for MPEG streaming ! if( !strcasecmp(content_type, "video/mpeg") ) { ! return DEMUXER_TYPE_MPEG_PS; ! } ! // AVI ??? => video/x-msvideo ! if( !strcasecmp(content_type, "video/x-msvideo") ) { ! return DEMUXER_TYPE_AVI; ! } ! } ! break; ! // Redirect ! case 301: // Permanently ! case 302: // Temporarily ! // TODO: RFC 2616, recommand to detect infinite redirection loops ! next_url = http_get_field( http_hdr, "Location" ); ! if( next_url!=NULL ) { ! close( fd ); ! url_free( url ); ! url = url_new( next_url ); ! redirect = 1; ! } ! break; ! default: ! printf("Server returned %d: %s\n", http_hdr->status_code, http_hdr->reason_phrase ); ! close( fd ); ! *fd_out=-1; ! return DEMUXER_TYPE_UNKNOWN; ! } } ! } while( redirect ); ! return DEMUXER_TYPE_UNKNOWN; ! } ! int ! nop_streaming_read( streaming_ctrl_t *streaming_ctrl ) { ! char *buffer; ! int len; ! if( streaming_ctrl==NULL ) return -1; ! len = streaming_ctrl->buffer->length; ! if( len==0 ) return 0; + buffer = (char*)malloc( len ); + if( buffer==NULL ) { + printf("Memory allocation failed\n"); + return -1; + } + net_fifo_pop( streaming_ctrl->buffer, buffer, len ); + write( streaming_ctrl->fd_pipe_in, buffer, len ); + free( buffer ); + return len; + } + + int + nop_streaming_start( streaming_ctrl_t *streaming_ctrl ) { + HTTP_header_t *http_hdr; + int fd; + if( streaming_ctrl==NULL ) return -1; + + fd = streaming_ctrl->fd_net; + if( fd<0 ) { + fd = http_send_request( *(streaming_ctrl->url) ); + if( fd<0 ) return -1; + http_hdr = http_read_response( fd ); + if( http_hdr==NULL ) return -1; + switch( http_hdr->status_code ) { case 200: // OK ! printf("Content-Type: [%s]\n", http_get_field(http_hdr, "Content-Type") ); ! printf("Content-Length: [%s]\n", http_get_field(http_hdr, "Content-Length") ); ! if( http_hdr->body_size>0 ) { ! write( streaming_ctrl->fd_pipe_in, http_hdr->body, http_hdr->body_size ); } break; default: ! printf("Server return %d: %s\n", http_hdr->status_code, http_hdr->reason_phrase ); close( fd ); ! fd = -1; } + streaming_ctrl->fd_net = fd; } + http_free( http_hdr ); + streaming_ctrl->streaming_read = nop_streaming_read; + streaming_ctrl->prebuffer_size = 180000; + // streaming_ctrl->prebuffer_size = 0; + streaming_ctrl->buffering = 1; + // streaming_ctrl->buffering = 0; + streaming_ctrl->status = streaming_playing_e; + return fd; + } + void ! network_streaming(void *arg) { ! char buffer[BUFFER_SIZE]; ! fd_set fd_net_in; int ret; ! ! arg = arg; ! do { ! FD_ZERO( &fd_net_in ); ! FD_SET( streaming_ctrl->fd_net, &fd_net_in ); ! ! ret = select( streaming_ctrl->fd_net+1, &fd_net_in, NULL, NULL, NULL ); ! if( ret<0 ) { ! perror("select"); ! exit(1); ! } ! if( FD_ISSET( streaming_ctrl->fd_net, &fd_net_in ) ) { ! ret = readFromServer( streaming_ctrl->fd_net, buffer, BUFFER_SIZE ); ! if( ret<=0 ) { ! streaming_ctrl->status=streaming_stopped_e; ! } else { ! //printf(" push: 0x%02X\n", *((unsigned int*)buffer) ); ! net_fifo_push( streaming_ctrl->buffer, buffer, ret ); ! if( !streaming_ctrl->buffering ) { ! do { ! ret = streaming_ctrl->streaming_read( streaming_ctrl ); ! if( ret<0 && streaming_ctrl->buffer->length<streaming_ctrl->prebuffer_size ) { ! // Need buffering ! streaming_ctrl->buffering = 1; ! } ! } while( streaming_ctrl->buffer->length>streaming_ctrl->prebuffer_size ); ! } else { ! if( streaming_ctrl->buffer->length>streaming_ctrl->prebuffer_size ) { ! streaming_ctrl->buffering = 0; ! printf("\n"); ! } else { ! printf(" Buffering: %d \%\r", (int)((float)(((float)streaming_ctrl->buffer->length)/((float)streaming_ctrl->prebuffer_size))*100) ); ! fflush(stdout); ! } ! } ! } ! } else { ! printf("Network fd not set\n"); ! } ! } while( streaming_ctrl->status==streaming_playing_e ); ! ! // Flush the buffer ! while( streaming_ctrl->buffer->length>0 ) { ! ret = streaming_ctrl->streaming_read( streaming_ctrl ); ! if( ret<0 ) break; ! } ! ! printf("Network thread done\n"); ! // Close to the pipe to stop mplayer. ! close( streaming_ctrl->fd_pipe_in ); ! } int streaming_start(URL_t **url, int fd, int streaming_type) { + int fd_pipe[2]; + // Open the pipe + if( pipe(fd_pipe)<0 ) { + printf("Pipe creation failed\n"); + return -1; + } + + streaming_ctrl = streaming_ctrl_new( ); + if( streaming_ctrl==NULL ) { + return -1; + } + streaming_ctrl->url = url; + streaming_ctrl->fd_pipe_in = fd_pipe[1]; + streaming_ctrl->fd_net = fd; + + #ifdef DUMP2FILE + { + int fd_file; + fd_file = open("dump.stream", O_WRONLY | O_CREAT ); + if( fd_file<0 ) { + perror("open"); + } + streaming_ctrl->fd_pipe_in = fd_file; + } + #endif + switch( streaming_type ) { ! case DEMUXER_TYPE_ASF: ! // Send the appropriate HTTP request ! fd = asf_http_streaming_start( streaming_ctrl ); break; ! case DEMUXER_TYPE_AVI: ! case DEMUXER_TYPE_MPEG_ES: ! case DEMUXER_TYPE_MPEG_PS: ! fd = nop_streaming_start( streaming_ctrl ); break; ! case DEMUXER_TYPE_UNKNOWN: default: printf("Unable to detect the streaming type\n"); close( fd ); + free( streaming_ctrl ); return -1; } ! if( fd<0 ) { ! free( streaming_ctrl ); ! return -1; ! } ! ! // Start the network thread ! if( pthread_create( &(streaming_ctrl->thread_id), NULL , (void*)network_streaming, (void*)NULL)<0 ) { ! printf("Unable to start the network thread.\n"); ! close( fd ); ! free( streaming_ctrl ); ! return -1; ! } ! printf("Network thread created with id: %d\n", streaming_ctrl->thread_id ); ! ! // streaming_ctrl->status = streaming_stopped_e; ! ! // return fd; ! return fd_pipe[0]; } int streaming_stop( ) { ! streaming_ctrl->status = streaming_stopped_e; ! return 0; } Index: network.h =================================================================== RCS file: /cvsroot/mplayer/main/network.h,v retrieving revision 1.2 retrieving revision 1.3 diff -C2 -r1.2 -r1.3 *** network.h 2001/05/29 17:03:17 1.2 --- network.h 2001/06/04 17:52:33 1.3 *************** *** 8,19 **** #define __NETWORK_H ! #define STREAMING_TYPE_UNKNOWN -1 ! #define STREAMING_TYPE_ASF 0 ! #define STREAMING_TYPE_MP3 1 #include "url.h" int connect2Server(char *host, int port); int autodetectProtocol( URL_t *url, int *fd_out ); --- 8,48 ---- #define __NETWORK_H ! #include <pthread.h> + #include "stream.h" #include "url.h" + #define BUFFER_SIZE 2048 + + typedef enum { + streaming_stopped_e, + streaming_playing_e + } streaming_status; + + typedef struct { + char *buffer; + int length; + } Net_Fifo; + + typedef struct streaming_control { + URL_t **url; + int fd_net; + int fd_pipe_in; + streaming_status status; + pthread_t thread_id; + Net_Fifo *buffer; + int buffering; // boolean + int prebuffer_size; + int (*streaming_read)( struct streaming_control *stream_ctrl ); + } streaming_ctrl_t; + + Net_Fifo* net_fifo_new( ); + void net_fifo_free(Net_Fifo *net_fifo ); + int net_fifo_pop(Net_Fifo *net_fifo, char *buffer, int length ); + int net_fifo_push(Net_Fifo *net_fifo, char *buffer, int length ); + int connect2Server(char *host, int port); + int readFromServer(int fd, char *buffer, int length ); int autodetectProtocol( URL_t *url, int *fd_out ); _______________________________________________ Mplayer-cvslog mailing list Mplayer-cvslog@lists.sourceforge.net http://lists.sourceforge.net/lists/listinfo/mplayer-cvslog