From fc3822a39693169d44c264dee5a42760c8e1138a Mon Sep 17 00:00:00 2001 From: bertrand Date: Mon, 4 Jun 2001 17:52:33 +0000 Subject: [PATCH] Continue implementation of the network streaming part. git-svn-id: svn://svn.mplayerhq.hu/mplayer/trunk@1000 b3059339-0415-0410-9bf9-f77b7e298cf2 --- network.c | 619 +++++++++++++++++++++++++++++++++++++++++++----------- network.h | 35 ++- 2 files changed, 524 insertions(+), 130 deletions(-) diff --git a/network.c b/network.c index 3ca48da711..c8fe06949a 100644 --- a/network.c +++ b/network.c @@ -4,30 +4,175 @@ * (C) 2001, MPlayer team. */ +#define DUMP2FILE + #include -#include +#include #include +#include +#include #include #include #include +#include + +#include #include #include #include #include +#include "stream.h" +#include "demuxer.h" + #include "network.h" #include "http.h" #include "url.h" #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"); return -1; @@ -36,7 +181,7 @@ connect2Server(char *host, int port) { if( isalpha(host[0]) ) { struct hostent *hp =(struct hostent*)gethostbyname( host ); if( hp==NULL ) { - printf("Unknown host: %s\n", host); + printf("Counldn't resolve name: %s\n", host); return -1; } memcpy( (void*)&server_address.sin_addr.s_addr, (void*)hp->h_addr, hp->h_length ); @@ -47,13 +192,74 @@ connect2Server(char *host, int port) { server_address.sin_port=htons(port); 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; + 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. int @@ -61,166 +267,325 @@ autodetectProtocol(URL_t *url, int *fd_out) { HTTP_header_t *http_hdr; int fd=-1; int i; + int redirect; char *extension; char *content_type; char *next_url; char response[1024]; -redo_request: - *fd_out=-1; - next_url = NULL; - extension = NULL; - content_type = NULL; + do { + *fd_out=-1; + next_url = NULL; + extension = NULL; + content_type = NULL; + redirect = 0; - if( url==NULL ) return STREAMING_TYPE_UNKNOWN; + 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; + // 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") ) { +// 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 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; + return DEMUXER_TYPE_ASF; } - 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 based protocol + if( !strcasecmp(url->protocol, "http") ) { + if( url->port==0 ) url->port = 80; - http_hdr = http_new_header(); - if( http_hdr==NULL ) { - close( fd ); - *fd_out=-1; - return STREAMING_TYPE_UNKNOWN; - } + fd = http_send_request( url ); + if( fd<0 ) { + *fd_out=-1; + return DEMUXER_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 ); + http_hdr = http_read_response( fd ); + if( http_hdr==NULL ) { close( fd ); *fd_out=-1; - return STREAMING_TYPE_UNKNOWN; + 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; + } } - } - return STREAMING_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() { +network_streaming(void *arg) { + char buffer[BUFFER_SIZE]; + fd_set fd_net_in; int ret; -/* - do { - ret = select( ); - } while( ); -*/ + 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->lengthprebuffer_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 STREAMING_TYPE_ASF: - // Send a the appropriate HTTP request - if( fd>0 ) close( fd ); - fd = asf_http_streaming_start( url ); + case DEMUXER_TYPE_ASF: + // Send the appropriate HTTP request + fd = asf_http_streaming_start( streaming_ctrl ); break; - case STREAMING_TYPE_MP3: - // Nothing else to do the server is already feeding the pipe. + case DEMUXER_TYPE_AVI: + case DEMUXER_TYPE_MPEG_ES: + case DEMUXER_TYPE_MPEG_PS: + fd = nop_streaming_start( streaming_ctrl ); break; - case STREAMING_TYPE_UNKNOWN: + case DEMUXER_TYPE_UNKNOWN: default: printf("Unable to detect the streaming type\n"); close( fd ); + free( streaming_ctrl ); return -1; } - return fd; + 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; } diff --git a/network.h b/network.h index c09358c2a0..4ef14500e7 100644 --- a/network.h +++ b/network.h @@ -7,14 +7,43 @@ #ifndef __NETWORK_H #define __NETWORK_H -#define STREAMING_TYPE_UNKNOWN -1 -#define STREAMING_TYPE_ASF 0 -#define STREAMING_TYPE_MP3 1 +#include +#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 ); #endif