in reply to Re: Serving video files in specific byte-ranges
in thread Serving video files in specific byte-ranges

Why on earth would I want to pretend??

Thank you both for tips about using xsendfile and the link to the RFC. But the rest is really not helpful. As I said, the goal is to "re-work this script so that I can serve video files in the requested byte ranges," not abandon the project in favor of HTTP authentication or "pretending" to support ranges.

I'll post back if & when I find the solution, so anyone else with my question might be helped.

  • Comment on Re^2: Serving video files in specific byte-ranges

Replies are listed 'Best First'.
Re^3: Serving video files in specific byte-ranges
by Corion (Patriarch) on Aug 31, 2011 at 14:16 UTC

    Have you tried seek together with the appropriate Content-Range headers in your response? What were the problems you encountered?

    Also see Simple HTTP in under 100 lines, which supports Range: queries and responses.

      That's a helpful link - thanks! Although, that code uses an HTTP::Response object to pass back to the daemon. Any advice on printing out the response object when not using a daemon? The CPAN docs are entirely clear on this (HTTP::Response and HTTP::Message), and when I implement the print $response->decoded_content in the following code, I get an error of Can't decode ref content at /usr/local/lib/perl5/site_perl/5.8.9/HTTP/Message.pm line 294.
      use HTTP::Response; use IO::File; my $response = new HTTP::Response( 404,undef,undef,"404 - Not foun +d." ); $|=1; my $file = new IO::File "< $path"; if (defined $file) { $response->code( 200 ); binmode $file; my $size = -s $file; my ($startrange, $endrange) = (0,$size-1); if (defined $ENV{HTTP_RANGE} and $ENV{HTTP_RANGE} =~ /bytes\s* +=\s*(\d+)-(\d+)?/) { $response->code( 206 ); ($startrange,$endrange) = ($1,$2 || $endrange); }; $file->seek($startrange,0); $response->header(Content_Length => $endrange-$startrange); $response->header(Content_Range => "bytes $startrange-$endrang +e/$size"); $response->content( sub { sysread($file, my ($buf), 16*1024); # No error checking ?? +? return $buf; }); }; print $response->decoded_content;

      Another option I tried, just printing the HTTP headers and content to STDOUT, follows below. This prints the requested ranges using a Transfer-Encoding method of chunked, as described here: w3.org and here: Wiki. There are no server-side errors using this script, but the iPhone Safari client still shows a broken file.

      my @ranges; if($ENV{HTTP_RANGE} =~ s/^bytes=//i) { @ranges = split /,/, $ENV{HTTP_RANGE}; print "Transfer-Encoding: chunked\n"; warn "printing ranges @ranges\n"; } else { print "Content-length: $filesize\n\n"; } if(@ranges) { open my $fh, '<', $path; foreach $range (@ranges) { my($begin, $end) = split /-/, $range; print "Content_Range: bytes $begin-$end/$filesize\ +n\n"; my $whence = ($begin)? 0 : 2; my $position = ($begin)? $begin : 0 - $end; seek $fh, $position, $whence; if($end) { $range = ($begin)? $end - $begin : $end; my $chunk_header = sprintf("%x", $range); print "$chunk_header\r\n"; sysread $fh, $_ , $range; print "$_\r\n"; warn "printed range of $range bytes from $begi +n to $end\n"; } else { while(<$fh>) { print } } } close $fh; print "0\r\n"; } else { open(FILE, $path) or die; while(<FILE>) { print } close(FILE); }

        Looking at the source of HTTP::Message, it just does not want to decode the content delivered by a callback:

        ... $content_ref = $self->content_ref; die "Can't decode ref content" if ref($content_ref) ne "SCALAR"; ...

        Either you fix HTTP::Message or you try it with ->content() instead of ->decoded_content. Alternatively, look at Plack as your server framework - it should handle callback responses more gracefully than your approach.

Re^3: Serving video files in specific byte-ranges
by BrowserUk (Patriarch) on Aug 31, 2011 at 14:45 UTC

    One of the problems you will encounter with this is that, because of the way video compression works, video files are not a consistent stream that can be arbitrarilly dipped into at random.

    Greatly simplified, the majority of video encoding work something like this.

    First, a full frame is transmitted. Then, a series of deltas -- subsets of full frames -- representing the changes from the previous full frame plus accumulated deltas is transmitted. At essentially arbitrary points dictated by some heuristic, a new full frame will be transmitted and the subsequent deltas will be applied to this new frame.

    If you dip into the stream at some random point, it is very unlikely that you will do so at exactly the start of a new frame. The result is that the receiver will never have seen the full frame to which the deltas you send should be applied and the you'll see the familiar scrambled video. This will remain until the stream encounters a new full frame.

    For active video -- where the camera is panning or zooming or both, this may only take a second or two.

    But for more static video -- examples: the fixed position of video chat or surveillance CCTV -- it may take many seconds before the compression algorithm detects sufficient change in the original input to make it economic to transmit a new full frame.

    This is why when trying to step back and forth through video streams, you can often only move in fairly course steps. Those that equate to new full frames.

    In essence, unless your client has some intelligence about what byte ranges it requests, the results are likely to be very unsatisfactory.


    Examine what is said, not who speaks -- Silence betokens consent -- Love the truth but pardon error.
    "Science is about questioning the status quo. Questioning authority".
    In the absence of evidence, opinion is indistinguishable from prejudice.