[2.0-Java] Hosting videos via HTML5 Video - Chrome video seeking not working correctly

362 views
Skip to first unread message

Neil Armstrong

unread,
Mar 13, 2014, 11:54:18 AM3/13/14
to play-fr...@googlegroups.com
Hi,

I am building a web application that will allow users to host videos though I have a problem that on Chrome (33.0.1750.149 - OSX) - while the video plays back fine - the HTML5 video element fails to seek correctly. I am using WebM for the video format.

The code shown below is what I am currently using to return the video file, it works perfectly fine on Firefox (27.0.1 - OSX) but has the seek problem on Chrome and fails to load completely on IE (11.0.9600.16518 - Win 8.1 - with WebM extensions installed).

    private static Result video(String fileSource) {
       
File videoFile = new File(fileSource);
       
String[] range = request().headers().get("Range");
       
if(range == null) {
           
return ok(new File(fileSource));
       
}
       
return videoStream(range[0], videoFile);
   
}
   
   
private static Result videoStream(String range, File videoFile) {
       
try {
           
String[] splitRange = range.substring("bytes=".length()).split("-");
           
long bytesToSkip = Long.parseLong(splitRange[0]);
           
if(bytesToSkip == 0) {
               
return ok(videoFile);
           
}
           
FileInputStream stream = new FileInputStream(videoFile);
            stream
.skip(bytesToSkip);
           
long totalBytes = getTotalBytes(splitRange, stream, bytesToSkip);
            response
().getHeaders().put(CONTENT_RANGE, String.format("bytes %d-%d/%d", bytesToSkip, totalBytes, totalBytes));
            response
().setHeader(ACCEPT_RANGES, "bytes");
            response
().setHeader(CONNECTION, "keep-alive");
           
return status(PARTIAL_CONTENT, stream);
       
} catch (IOException e) {
            e
.printStackTrace();
           
return notFound();
       
}
   
}

Am I missing anything to correctly handle video streaming to a HTML5 video element? If you need anymore information just ask.

Thanks,
Neil.

Steven Marcus

unread,
Mar 16, 2014, 6:52:30 PM3/16/14
to play-fr...@googlegroups.com
Hi!

Play doesn't support byte range requests and I suspect that the code you are using doesn't conform to the http spec for byte range requests.
You can find another approach to fixing this problem here: https://github.com/playframework/playframework/issues/1097

The impression I have is that the focus for Play is not for use as a general static file web server.
The Assets controller makes some assumptions about serving "small" content from memory, and doesn't support byte range requests as above.

My suggestion is to use a reverse proxy in front of Play. You can configure the reverse proxy to forward your page and api requests to Play, and have the reverse proxy serve other static files directly. If you use nginx as your reverse proxy you can serve video files efficiently on modest hardware.

(Terminating your SSL endpoint at a reverse proxy is a good way to work around Play's unreliable SSL handling. Although SSL in Play may have been fixed.)

Hope that helps!
Message has been deleted

Neil Armstrong

unread,
Mar 17, 2014, 7:15:55 AM3/17/14
to play-fr...@googlegroups.com
I managed to fix it, feel rather silly now. The CONTENT_RANGE requires { bytesToSkip, totalBytes - 1, totalBytes }. Here is the working code for anyone else that has this problem:

    private static Result video(String fileSource) {
       
File videoFile = new File(fileSource);
       
String[] range = request().headers().get("Range");
       
if(range == null) {
           
return ok(new File(fileSource));
       
}
       
return videoStream(range[0], videoFile);
   
}
   
   
private static Result videoStream(String range, File videoFile) {
       
try {
           
String[] splitRange = range.substring("bytes=".length()).split("-");
           
long bytesToSkip = Long.parseLong(splitRange[0]);
           
if(bytesToSkip == 0) {
               
return ok(videoFile);
           
}
           
FileInputStream stream = new FileInputStream(videoFile);
            stream
.skip(bytesToSkip);
           
long totalBytes = getTotalBytes(splitRange, stream, bytesToSkip);

            response
().getHeaders().put(CONTENT_RANGE, String.format("bytes %d-%d/%d", bytesToSkip, totalBytes - 1, totalBytes));

goran.v...@whut.com

unread,
Dec 7, 2017, 3:52:18 PM12/7/17
to Play Framework
What does your getTotalBytes(splitRange, stream, bytesToSkip) look like?

Matthias Kurz

unread,
Dec 7, 2017, 4:03:23 PM12/7/17
to Play Framework
FYI: Play has Range requests support out of the box since Play 2.5.3
Reply all
Reply to author
Forward
0 new messages