Beefy Boxes and Bandwidth Generously Provided by pair Networks
"be consistent"

Serving video files in specific byte-ranges

by goofball (Acolyte)
on Aug 26, 2011 at 15:00 UTC ( #922667=perlquestion: print w/replies, xml ) Need Help??
goofball has asked for the wisdom of the Perl Monks concerning the following question:

Hello, monks!

Seeking your help, as the cosmic entity known as Google seems to be keeping secrets from me on this topic.

I have a web site that serves video files, but I can't just leave the videos in an un-protected directory because only logged-in users must be able to access them. So I wrote a script, which includes the below snippets, to authenticate the user and then read the video file from disc and "print" that data line-by-line as output. This works great for most applications, but now that I'm trying to serve files to mobile devices like iPhone, this method no longer works. In my research I've learned this is because the mobile browser/video player asks the server for certain data ranges of the video file via multiple requests. Any modern HTTP server handles this behavior automatically, but when my script fails to serve the video fie in the requested chunks, the mobile device sees the file as broken and will not play it.

Could anyone please advise me on how to re-work this script so that I can serve video files in the requested byte ranges?

Many thanks.

print "Content-type: video/mp4\n"; print "Content-length: $filesize\n\n"; open(FILE, $path) or die; while(<FILE>) { print } close(FILE);

Or, alternatively:

open my $fh , '<', $path; print $_ while ( sysread $fh, $_ , 8192 ); close $fh;

Unfortunately, neither of these alternatives is working for mobile clients, although both work fine when the client is a desktop browser.

K.I.S.S. me, I'm stupid!

Replies are listed 'Best First'.
Re: Serving video files in specific byte-ranges
by Anonymous Monk on Aug 26, 2011 at 15:44 UTC

    This is a common problem.

    You should have used HTTP authentication from the beginning. Go with HTTP's strengths, not against it.

    You're probably logging in user with forms and cookies instead. If you want to keep that system, make up URLs to the video for each user, perhaps set it up so that such a URL expires after a while. Use an internal redirect and let your Web server deal with the static file, it knows how to deal with ranges/requests for partial content. The xsendfile extension helps you with that and exist for Apache httpd, nginx and lighttpd. You can then throw your Perl code away.

Re: Serving video files in specific byte-ranges
by Anonymous Monk on Aug 26, 2011 at 22:24 UTC

    Seeking your help, as the cosmic entity known as Google seems to be keeping secrets from me on this topic

    Its called RFC :)

    If your program doesn't support ranges, don't pretend like you do, return an error

    Or if you're intent on writing support, maybe you can glean enough of RFC from HTTP::Range

      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.

        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.

        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.

Log In?

What's my password?
Create A New User
Node Status?
node history
Node Type: perlquestion [id://922667]
Approved by blue_cowdawg
and all is quiet...

How do I use this? | Other CB clients
Other Users?
Others taking refuge in the Monastery: (5)
As of 2018-03-22 01:16 GMT
Find Nodes?
    Voting Booth?
    When I think of a mole I think of:

    Results (272 votes). Check out past polls.