The MU forums have moved to WordPress.org

Byte-range file downloads (iPhone/iPod support) (4 posts)

  1. cjgibbs
    Member
    Posted 14 years ago #

    This took me forever to figure out so I wanted to share the solution.

    The problem:
    Audio/video files uploaded to wordpress mu blogs would not play on the iPhone/iPod touch because the WPMU file handler does not support byte-range downloading.

    The solution:
    Taken straight from Thomas Thomassen's PHP Download Server Script (so he gets all the credit here), remove the readline() call at the bottom of wp-content/blogs.php

    Then add the following in it's place:

    function range_download($file) {
    
    	$fp = @fopen($file, 'rb');
    
    	$size   = filesize($file); // File size
    	$length = $size;           // Content length
    	$start  = 0;               // Start byte
    	$end    = $size - 1;       // End byte
    	// Now that we've gotten so far without errors we send the accept range header
    	/* At the moment we only support single ranges.
    	 * Multiple ranges requires some more work to ensure it works correctly
    	 * and comply with the spesifications: http://www.w3.org/Protocols/rfc2616/rfc2616-sec19.html#sec19.2
    	 *
    	 * Multirange support annouces itself with:
    	 * header('Accept-Ranges: bytes');
    	 *
    	 * Multirange content must be sent with multipart/byteranges mediatype,
    	 * (mediatype = mimetype)
    	 * as well as a boundry header to indicate the various chunks of data.
    	 */
    	header("Accept-Ranges: 0-$length");
    	// header('Accept-Ranges: bytes');
    	// multipart/byteranges
    	// http://www.w3.org/Protocols/rfc2616/rfc2616-sec19.html#sec19.2
    	if (isset($_SERVER['HTTP_RANGE'])) {
    
    		$c_start = $start;
    		$c_end   = $end;
    		// Extract the range string
    		list(, $range) = explode('=', $_SERVER['HTTP_RANGE'], 2);
    		// Make sure the client hasn't sent us a multibyte range
    		if (strpos($range, ',') !== false) {
    
    			// (?) Shoud this be issued here, or should the first
    			// range be used? Or should the header be ignored and
    			// we output the whole content?
    			header('HTTP/1.1 416 Requested Range Not Satisfiable');
    			header("Content-Range: bytes $start-$end/$size");
    			// (?) Echo some info to the client?
    			exit;
    		}
    		// If the range starts with an '-' we start from the beginning
    		// If not, we forward the file pointer
    		// And make sure to get the end byte if spesified
    		if ($range0 == '-') {
    
    			// The n-number of the last bytes is requested
    			$c_start = $size - substr($range, 1);
    		}
    		else {
    
    			$range  = explode('-', $range);
    			$c_start = $range[0];
    			$c_end   = (isset($range[1]) && is_numeric($range[1])) ? $range[1] : $size;
    		}
    		/* Check the range and make sure it's treated according to the specs.
    		 * http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html
    		 */
    		// End bytes can not be larger than $end.
    		$c_end = ($c_end > $end) ? $end : $c_end;
    		// Validate the requested range and return an error if it's not correct.
    		if ($c_start > $c_end || $c_start > $size - 1 || $c_end >= $size) {
    
    			header('HTTP/1.1 416 Requested Range Not Satisfiable');
    			header("Content-Range: bytes $start-$end/$size");
    			// (?) Echo some info to the client?
    			exit;
    		}
    		$start  = $c_start;
    		$end    = $c_end;
    		$length = $end - $start + 1; // Calculate new content length
    		fseek($fp, $start);
    		header('HTTP/1.1 206 Partial Content');
    	}
    	// Notify the client the byte range we'll be outputting
    	header("Content-Range: bytes $start-$end/$size");
    	header("Content-Length: $length");
    
    	// Start buffered download
    	$buffer = 1024 * 8;
    	while(!feof($fp) && ($p = ftell($fp)) <= $end) {
    
    		if ($p + $buffer > $end) {
    
    			// In case we're only outputtin a chunk, make sure we don't
    			// read past the length
    			$buffer = $end - $p + 1;
    		}
    		set_time_limit(0); // Reset time limit for big files
    		echo fread($fp, $buffer);
    		flush(); // Free up memory. Otherwise large files will trigger PHP's memory limit.
    	}
    
    	fclose($fp);
    
    }
    
    if (isset($_SERVER['HTTP_RANGE']))  {
    	range_download($file);
    } else {
    	readfile( $file );
    }

    Now audio and video files uploaded to wordpress mu blogs should be playable via iPhone and iPod touch, or any other device that supports byte-range downloads.

  2. andrea_r
    Moderator
    Posted 14 years ago #

    If you want to see it as a fix rolled into core, file it as a trac ticket here:
    http://trac.mu.wordpress.org/

    Same user/pass as the forums.

  3. cjgibbs
    Member
    Posted 14 years ago #

    Thanks andrea, it's now filed at:
    http://trac.mu.wordpress.org/ticket/1128

  4. josswinn
    Member
    Posted 14 years ago #

    Just wanted to say +1 for this. Works nicely for me and was a fix that's really helped me out. Thanks.

About this Topic

  • Started 14 years ago by cjgibbs
  • Latest reply from josswinn