Serving video files from Play 2.1

httpresponse, playframework, playframework-2.0, playframework-2.1, streaming

Solution

Reasons why Chrome cancels requests.

The sources you used are more complepete. I show you with code what I spotted:

The response code is not always 206 PARTIAL_CONTENT:

val responseCode = if (rangeStart != 0 || rangeEnd != fileLength - 1) 206 else 200

Skipping the bytes is missing:

val stream = new FileInputStream(file)
stream.skip(rangeStart) # range starts defaults to 0

The content range can be the full file and partial:

val contentLength = if (responseCode == 206) (rangeEnd - rangeStart + 1) else fileLength

So you should not send on every case send the Content-Range header.

Problem

I'm attempting to create a controller method that serves a video file backed by some CMS-like database entry. My controller method looks like this: ``` def getVideo(id: Int) = DBAction { request => implicit dbSession => { for { dbFile <- fetchDBFile(id) fsFile <- fetchFilesystemFile(dbFile) rangeOpt <- request.headers.get(RANGE).map(_.replaceAll("bytes=", "").split("-").toList match { case rangeStart :: rangeEnd :: Nil => Some(rangeStart.toLong, rangeEnd.toLong) case rangeStart :: Nil => Some(rangeStart.toLong, fsFile.length()) case _ => None }) (rangeStart, rangeEnd) <- rangeOpt } yield SimpleResult( header = ResponseHeader( status = PARTIAL_CONTENT, headers = Map( CONTENT_TYPE -> MimeTypes.forExtension("mp4").get, ACCEPT_RANGES -> "bytes", DATE -> new SimpleDateFormat("EEE, dd MMM yyyy HH:mm:ss zzz").format(new Date()), CONTENT_LENGTH -> fsFile.length.toString, CONTENT_RANGE -> s"bytes $rangeStart-$rangeEnd/${fsFile.length}", CONNECTION -> "keep-alive" ) ), body = Enumerator.fromStream(new FileInputStream(fsFile)) ) } getOrElse { NotFound } } ``` It's mostly based on two sources to implement the logic to handle the specific byte range request necessary to serve videos. When using either Chrome or Safari on OS X to access this controller method, the developer tools report that the request is cancelled - no response, be it a 200 or 404, is received. I have confirmed that the SimpleResponse is actually returned by this controller action on requests I expect it to deliver a good response, but either Play won't finish the response or my browsers won't accept it. Am I doing something wrong here in response, or have I stumbled upon a bug in the framework? My Play version is 2.1.3.

Original source

Related problems