Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Chrome fails to load video if transferred with status 206 Partial Content

I'm making a simple Java com.sun.net.httpserver.HttpServer to serve static video files. If I return status code 206, Partial Content, when I try access it through the browser the video is not able to play (the video plays fine with status code 200 but I wanted to be able to seek and loop the video), this is my HttpHandler:

final String path = StaticHandler.toPathSafe(httpExchange.getRequestURI().getPath());
System.out.println(path);
final File file = new File(path);

if (file.isFile())
{
    int code = 200;
    long position = 0L;
    long end = file.length();

    if (httpExchange.getRequestHeaders().containsKey("Range"))
    {
        try
        {
            long[] range = StaticHandler.parseRange(httpExchange.getRequestHeaders().get("Range").get(0));

            position = range[0];
            if (range[1] != -1)
                end = range[1];

            // the video loads fine when code = 200;
            code = 206;

            httpExchange.getResponseHeaders().set("Content-Range", "bytes " + position + "-" + end + "/" + file.length());
        }
        catch (Exception e)
        {
            e.printStackTrace();
        }
    }

    httpExchange.getResponseHeaders().set("Accept-Range", "bytes");
    httpExchange.getResponseHeaders().set("Content-Type", "video/mp4");
    httpExchange.getResponseHeaders().set("Content-Length", String.valueOf(end - position));

    System.out.println("Response: " + position + ", " + end);

    httpExchange.sendResponseHeaders(code, 0L);

    final FileChannel fileChannel = new FileInputStream(file).getChannel();
    final WritableByteChannel responseChannel = Channels.newChannel(response.getOutputStream());
    fileChannel.transferTo(position, end - position, responseChannel);

    responseChannel.close();
    fileChannel.close();
}
else
{
    System.out.println("404");
    httpExchange.sendResponseHeaders(404, -1);
}

The above code fails to load on chrome but works fine in firefox, here are the headers I get in Chrome:

Response Headers:

Accept-range: bytes
Content-length: 31491166
Content-range: bytes 0-31491166/31491166
Content-type: video/mp4
Date: Sat, 27 Jul 2019 14:32:55 GMT
Transfer-encoding: chunked


Request Headers:

Accept: */*
Accept-Encoding: identity;q=1, *;q=0
Accept-Language: en-US,en;q=0.9
Cache-Control: no-cache
Connection: keep-alive
Host: 192.168.56.1:5000
Pragma: no-cache
Range: bytes=0-
Referer: http://192.168.56.1:5000/30MB.mp4
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/75.0.3770.142 Safari/537.36

Am I missing something and/or is there something wrong in my code?

like image 421
Fr3ddyDev Avatar asked Oct 19 '25 13:10

Fr3ddyDev


2 Answers

Range is inclusive start-end. Not start-length

For example:

Content-range: bytes 0-0/31491166

Returns 1 byte (from byte zero to byte zero)

Your code doesn’t work because the browser is waiting for one more byte that is never sent.

like image 112
szatmary Avatar answered Oct 21 '25 03:10

szatmary


Use below code snippet. it's working and tested on all browsers Safari, Firefox, and Chrome.

protected byte[] prepareContent(final HttpHeaders headers, byte[] media,
        String range) throws IOException {
    
    long rangeStart = 0;
    long rangeEnd;

    long fileSize = media.length;
    String[] ranges = range.split("-");
    rangeStart = Long.parseLong(ranges[0].substring(6));

    if (ranges.length > 1) {
        rangeEnd = Long.parseLong(ranges[1]);
    } else {
        rangeEnd = fileSize - 1;
    }
    if (fileSize < rangeEnd) {
        rangeEnd = fileSize - 1;
    }
    
    String contentLength = String.valueOf((rangeEnd - rangeStart) + 1);
    
    headers.add("Content-Length", contentLength);
    headers.add("Content-Range", "bytes " + rangeStart + "-" + rangeEnd + "/" + fileSize);
    headers.add("Content-Type", "video/mp4");
    headers.add("Accept-Ranges","bytes");
    return readByteRange(media, rangeStart, rangeEnd);
}

private byte[] readByteRange(byte[] media, long start, long end) throws IOException {
    
    try (InputStream inputStream = new ByteArrayInputStream(media);
         ByteArrayOutputStream bufferedOutputStream = new ByteArrayOutputStream()) {
        
        int nRead;
        while ((nRead = inputStream.read(media, 0, media.length)) != -1) {
            bufferedOutputStream.write(media, 0, nRead);
        }
        bufferedOutputStream.flush();
        byte[] result = new byte[(int) (end - start) + 1];
        System.arraycopy(bufferedOutputStream.toByteArray(), (int) start, result, 0, result.length);
        return result;
    }
}
like image 45
Atul Jain Avatar answered Oct 21 '25 03:10

Atul Jain



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!