>From 981f7039cb07f87ac9f7b9a9611c5ed7399bc8ee Mon Sep 17 00:00:00 2001 From: Bill C Riemers Date: Thu, 12 Jun 2008 10:31:47 -0400 Subject: [PATCH] Patch to fix bug #1532, and improve http seek times. --- modules/access/http.c | 184 ++++++++++++++++++++++++++++++++++++++++--------- 1 files changed, 150 insertions(+), 34 deletions(-) diff --git a/modules/access/http.c b/modules/access/http.c index 8a04e14..efe91f0 100644 --- a/modules/access/http.c +++ b/modules/access/http.c @@ -58,6 +58,23 @@ static int Open ( vlc_object_t * ); static void Close( vlc_object_t * ); +/* + * The minimum amount of data to read in a single request. This should be + * on the order of 60ms worth of data for medium quality 480p divx video. + */ +#define HTTP_MIN_BLOCKSIZE 0x4000L +/* + * The maximum amount of data to read in a 'fast seek' request. + * This corresponds to about half a second of medium quality + * 480p divx video. + */ +#define HTTP_SEEK_BLOCKSIZE 0x20000L +/* + * The maximum amount of data to read in a single request. This corresponds + * to about 8 seconds medium quality 480p divx video. + */ +#define HTTP_MAX_BLOCKSIZE 0x200000L + #define PROXY_TEXT N_("HTTP proxy") #define PROXY_LONGTEXT N_( \ "HTTP proxy to be used It must be of the form " \ @@ -178,7 +195,8 @@ struct access_sys_t char *psz_icy_genre; char *psz_icy_title; - int i_remaining; + int64_t i_remaining; + int64_t i_blocksize; bool b_seekable; bool b_reconnect; @@ -195,6 +213,7 @@ static int OpenWithCookies( vlc_object_t *p_this, vlc_array_t *cookies ); static ssize_t Read( access_t *, uint8_t *, size_t ); static ssize_t ReadCompressed( access_t *, uint8_t *, size_t ); static int Seek( access_t *, int64_t ); +static int SkipForward( access_t *, int64_t ); static int Control( access_t *, int, va_list ); /* */ @@ -268,7 +287,7 @@ static int OpenWithCookies( vlc_object_t *p_this, vlc_array_t *cookies ) p_sys->psz_icy_genre = NULL; p_sys->psz_icy_title = NULL; p_sys->i_remaining = 0; - + p_sys->i_blocksize = HTTP_MIN_BLOCKSIZE; p_sys->cookies = saved_cookies; /* Parse URI - remove spaces */ @@ -588,7 +607,7 @@ static ssize_t Read( access_t *p_access, uint8_t *p_buffer, size_t i_len ) } if( p_access->info.i_size > 0 && - i_len + p_access->info.i_pos > p_access->info.i_size ) + (int64_t)(i_len + p_access->info.i_pos) > (int64_t)p_access->info.i_size ) { if( ( i_len = p_access->info.i_size - p_access->info.i_pos ) == 0 ) { @@ -626,21 +645,41 @@ static ssize_t Read( access_t *p_access, uint8_t *p_buffer, size_t i_len ) } } - if( i_len > p_sys->i_chunk ) + if( (int64_t)i_len > p_sys->i_chunk ) { - i_len = p_sys->i_chunk; + i_len = (size_t)p_sys->i_chunk; } } - if( p_sys->b_continuous && (ssize_t)i_len > p_sys->i_remaining ) + if(i_len <= 0) { + return 0; + } + if ( ! p_sys->b_continuous ) { + if(p_sys->i_remaining == 0) { + if ( Request( p_access, p_access->info.i_pos) != VLC_SUCCESS) { + int64_t i_pos = p_access->info.i_pos; + Disconnect( p_access ); + if ( Connect( p_access, i_pos ) ) { + p_access->info.b_eof = true; + return 0; + } + } + } + if( ((int64_t)i_len > p_sys->i_remaining) && (p_sys->i_remaining > 0) ) { + i_len = (size_t)p_sys->i_remaining; + } + } + if( p_sys->b_continuous && (p_sys->i_remaining > 0 ) && ((int64_t)i_len > p_sys->i_remaining) ) { /* Only ask for the remaining length */ int i_new_len = p_sys->i_remaining; if( i_new_len == 0 ) { - Request( p_access, 0 ); - i_read = Read( p_access, p_buffer, i_len ); - return i_read; + //bcr this makes no sense + //bcr Request( p_access, 0 ); + //bcr i_read = Read( p_access, p_buffer, i_len ); + //bcr return i_read; + return 0; } i_len = i_new_len; } @@ -658,8 +697,8 @@ static ssize_t Read( access_t *p_access, uint8_t *p_buffer, size_t i_len ) return -1; } } - if( i_len > i_next ) - i_len = i_next; + if( (int64_t)i_len > i_next ) + i_len = (size_t)i_next; } i_read = net_Read( p_access, p_sys->fd, p_sys->p_vs, p_buffer, i_len, false ); @@ -713,10 +752,7 @@ static ssize_t Read( access_t *p_access, uint8_t *p_buffer, size_t i_len ) if( i_read == 0 ) p_access->info.b_eof = true; } - if( p_sys->b_continuous ) - { - p_sys->i_remaining -= i_read; - } + p_sys->i_remaining -= i_read; return i_read; } @@ -822,21 +858,83 @@ static ssize_t ReadCompressed( access_t *p_access, uint8_t *p_buffer, #endif /***************************************************************************** - * Seek: close and re-open a connection at the right place + * SkipForward: Position ourself forward by i_len bytes by reading and if + * neccessary submitting an new request. + *****************************************************************************/ +static int SkipForward( access_t *p_access, int64_t i_len ) { + access_sys_t *p_sys = p_access->p_sys; + int64_t i_tell = p_access->info.i_pos + i_len; + if(p_sys->i_remaining > 0 ) { + size_t j_len = (size_t)((i_len > p_sys->i_remaining)?p_sys->i_remaining:i_len); + uint8_t *p_buffer = (uint8_t *)malloc(j_len); + do { + ssize_t i_read = Read(p_access, p_buffer, j_len); + if (i_read == 0 || i_read == (ssize_t)(-1)) { + free(p_buffer); + return VLC_EGENERIC; + } + j_len -= i_read; + } while ( j_len > 0); + free(p_buffer); + } + if(i_tell == p_access->info.i_pos) { + return VLC_SUCCESS; + } + p_sys->i_blocksize = HTTP_MIN_BLOCKSIZE; + return Request( p_access, i_tell); +} + +/***************************************************************************** + * Seek: Attempt to seek by a small read or offseting the position of the + * next block. If that fails close and re-open a connection at the right place. *****************************************************************************/ static int Seek( access_t *p_access, int64_t i_pos ) { - msg_Dbg( p_access, "trying to seek to %"PRId64, i_pos ); + msg_Dbg( p_access, "trying to seek to %"PRId64"/"PRId64, i_pos, p_access->info.i_size ); + if( i_pos < 0 ) + { + msg_Err( p_access, "seeking too early" ); + return Seek( p_access, 0); + } + if( i_pos > p_access->info.i_size ) + { + msg_Err( p_access, "seeking too far" ); + // Fix me: This replicates the file.c behavior of returning + // success even when seeking too far. + return Seek( p_access, p_access->info.i_size ); + } + // we can use the skip data to seek forward + access_sys_t *p_sys = p_access->p_sys; + if( i_pos > p_access->info.i_pos) { + int64_t i_needed = i_pos - p_access->info.i_pos; + if( (i_needed <= HTTP_SEEK_BLOCKSIZE)||(p_sys->i_remaining <= HTTP_SEEK_BLOCKSIZE) ) { + SkipForward(p_access, i_needed ); + } + } + if ( i_pos == p_access->info.i_pos ) { + return VLC_SUCCESS; + } + // Now we try skipping the remaining bytes and then making the next + // request with our desired offset. + if( (p_sys->i_remaining > 0) && (p_sys->i_remaining <= HTTP_SEEK_BLOCKSIZE)) { + if ( SkipForward( p_access, p_sys->i_remaining) == VLC_SUCCESS ) { + p_sys->i_blocksize = HTTP_MIN_BLOCKSIZE; + if (Request(p_access, i_pos) == VLC_SUCCESS) { + return VLC_SUCCESS; + } + } + } + // Since the server does not have to honour our keep-alive request + // we could end-up here. Disconnect( p_access ); - - if( Connect( p_access, i_pos ) ) + int retval= Connect( p_access, i_pos ); + if (retval != VLC_SUCCESS ) { msg_Err( p_access, "seek failed" ); p_access->info.b_eof = true; - return VLC_EGENERIC; } - return VLC_SUCCESS; + return retval; } /***************************************************************************** @@ -859,7 +957,8 @@ static int Control( access_t *p_access, int i_query, va_list args ) break; case ACCESS_CAN_FASTSEEK: pb_bool = (bool*)va_arg( args, bool* ); - *pb_bool = false; + // http 1.1 should be just as fast as nfs... + *pb_bool = p_sys->b_seekable; break; case ACCESS_CAN_PAUSE: case ACCESS_CAN_CONTROL_PACE: @@ -945,6 +1044,7 @@ static int Connect( access_t *p_access, int64_t i_tell ) p_sys->psz_icy_name = NULL; p_sys->psz_icy_genre = NULL; p_sys->psz_icy_title = NULL; + p_sys->i_blocksize = HTTP_MIN_BLOCKSIZE; p_access->info.i_size = 0; p_access->info.i_pos = i_tell; @@ -1086,11 +1186,19 @@ static int Request( access_t *p_access, int64_t i_tell ) /* User Agent */ net_Printf( VLC_OBJECT(p_access), p_sys->fd, pvs, "User-Agent: %s\r\n", p_sys->psz_user_agent ); + p_access->info.i_pos = i_tell; /* Offset */ if( p_sys->i_version == 1 ) { + int64_t i_end=i_tell+(int64_t)(p_sys->i_blocksize-1); + if((p_access->info.i_size > 0) && (i_end >= p_access->info.i_size)) { + i_end = p_access->info.i_size-1; + } + else if ((p_sys->i_blocksize *= 2) > HTTP_MAX_BLOCKSIZE) { + p_sys->i_blocksize = HTTP_MAX_BLOCKSIZE; + } net_Printf( VLC_OBJECT(p_access), p_sys->fd, pvs, - "Range: bytes=%"PRId64"-\r\n", i_tell ); + "Range: bytes=%"PRId64"-%"PRId64"\r\n", i_tell, i_end ); } /* Cookies */ @@ -1131,16 +1239,8 @@ static int Request( access_t *p_access, int64_t i_tell ) net_Printf( VLC_OBJECT(p_access), p_sys->fd, pvs, "Icy-MetaData: 1\r\n" ); - if( p_sys->b_continuous ) - { - net_Printf( VLC_OBJECT( p_access ), p_sys->fd, pvs, + net_Printf( VLC_OBJECT( p_access ), p_sys->fd, pvs, "Connection: Keep-Alive\r\n" ); - } - else if( p_sys->i_version == 1 ) - { - net_Printf( VLC_OBJECT( p_access ), p_sys->fd, pvs, - "Connection: Close\r\n"); - } if( net_Printf( VLC_OBJECT(p_access), p_sys->fd, pvs, "\r\n" ) < 0 ) { @@ -1196,6 +1296,7 @@ static int Request( access_t *p_access, int64_t i_tell ) } free( psz ); + p_sys->i_remaining = 0; for( ;; ) { char *psz = net_Gets( VLC_OBJECT(p_access), p_sys->fd, pvs ); @@ -1236,13 +1337,28 @@ static int Request( access_t *p_access, int64_t i_tell ) p_access->info.i_size = -1; msg_Dbg( p_access, "this frame size=%lld", atoll(p ) ); p_sys->i_remaining = atoll( p ); + p_access->info.i_pos = 0; } else { - p_access->info.i_size = i_tell + atoll( p ); - msg_Dbg( p_access, "stream size=%"PRId64, p_access->info.i_size ); + p_access->info.i_pos = i_tell; + p_sys->i_remaining = atoll( p ); + if(p_access->info.i_size <= 0) { + p_access->info.i_size = i_tell + p_sys->i_remaining; + msg_Dbg( p_access, "stream size=%"PRId64, p_access->info.i_size ); + } } } + else if(! strcasecmp( psz, "Content-Range") ) { + int64_t j_tell = i_tell; + int64_t j_end = (i_tell + p_sys->i_remaining - 1); + int64_t j_size = p_access->info.i_size; + sscanf(p,"bytes %"PRId64"-%"PRId64"/%"PRId64,&j_tell,&j_end,&j_size); + p_access->info.i_size = j_size; + p_access->info.i_pos = j_tell; + p_sys->i_remaining = j_end+1-j_tell; + msg_Dbg( p_access, "stream size=%"PRId64",position=%"PRId64",remaining=%"PRId64,j_size,j_tell,p_sys->i_remaining); + } else if( !strcasecmp( psz, "Location" ) ) { char * psz_new_loc; -- 1.5.5.1