>From 981f7039cb07f87ac9f7b9a9611c5ed7399bc8ee Mon Sep 17 00:00:00 2001
From: Bill C Riemers <briemers@redhat.com>
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

