How to start play .mkv and .mp4 videos on Mi Video using NanoHttpd without reading the entire file (Android)

255 views Asked by At

I'm using NanoHttpd library in order to serve video files of type .mkv and .mp4 on Mi Video.

As the reading of the input stream was too slow, I changed NanoHttpd's reading buffer size to 1M:

private void sendBody(OutputStream outputStream, long pending) throws IOException {
    long BUFFER_SIZE = 1048576L; // Instead of 16384
    byte[] buff = new byte[(int)BUFFER_SIZE];
    boolean sendEverything = pending == -1L;

    while(pending > 0L || sendEverything) {
        long bytesToRead = sendEverything ? BUFFER_SIZE : Math.min(pending, BUFFER_SIZE);
        int read = this.data.read(buff, 0, (int)bytesToRead);
        if (read <= 0) {
           break;
        }

        outputStream.write(buff, 0, read);
        if (!sendEverything) {
           pending -= (long)read;
        }
    }
}

I changed inputStream.skip() the same way:

public long skip(long n) throws IOException {

    long remaining = n;
    int nr;

    if (n <= 0) {
        return 0;
    }

    int size = (int)Math.min(1048576L, remaining); \\ Instead of 2048
    byte[] skipBuffer = new byte[size];
    while (remaining > 0) {
        nr = read(skipBuffer, 0, (int)Math.min(size, remaining));
        if (nr < 0) {
            break;
        }
        remaining -= nr;
    }

    return n - remaining;
}

I override NanoHttpd.serve():

@Override
public Response serve(IHTTPSession session) {
    ... // Getting the inputStream, length and mimeType

    String range = null;
    for (String key : session.getHeaders().keySet()) {
        if ("range".equals(key)) {
            range = session.getHeaders().get(key);
        }
    }
    
    if (null != range) {
        return createPartialResponse(mimeType, inputStream, length, range);
    } else {
        return createFullResponse(mimeType, inputStream, length);
    }
}

private Response createFullResponse(String mimeType, InputStream inputStream, long length) {
    return newFixedLengthResponse(Response.Status.OK, mimeType, inputStream, length);
}

private Response createPartialResponse(String mimeType, InputStream inputStream, long length, String rangeHeader) throws IOException {
    Response response = null;
    String rangeValue = rangeHeader.trim().substring("bytes=".length());
    long lastIndex = length - 1;
    long startIndex, endIndex;
    
    if (rangeValue.startsWith("-")) { // Range: bytes=-<suffix-length>
        endIndex = lastIndex;
        startIndex = lastIndex - Long.parseLong(rangeValue.substring("-".length()));
    } else { // Range: bytes=<range-start>-  OR  bytes=<range-start>-<range-end>  OR  bytes=<range-start>-<range-end>, <range-start>-<range-end>
        String[] range = rangeValue.split("-");
        startIndex = Long.parseLong(range[0]);
        endIndex = range.length > 1 ? Long.parseLong(range[1]) : lastIndex;
    }
    
    if (endIndex > lastIndex) {
        endIndex = lastIndex;
    }
    
    if (startIndex <= endIndex) {
        long newLength = endIndex - startIndex + 1;
        inputStream.skip(startIndex);
        response = newFixedLengthResponse(Response.Status.PARTIAL_CONTENT, mimeType, inputStream, newLength);
        response.addHeader("Content-Length", String.valueOf(newLength));
        response.addHeader("Content-Range", "bytes " + startIndex + "-" + endIndex + "/" + length); // "bytes start-end/length"
    } else {
        response = newFixedLengthResponse(Response.Status.RANGE_NOT_SATISFIABLE, MIME_PLAINTEXT, rangeHeader);
        response.addHeader("Content-Range", "bytes 0-0/" + length); // "bytes 0-0/length"
    }
    
    response.addHeader("Accept-Ranges", "bytes"); // Announce that the file server accepts partial content requests
    return response;
}

With Mi Video, small sizes of .mkv (98.5 MB) and .mp4 (77.9 MB) had to be read completely before they could be played (~15 seconds). But, large file sizes of nearly 1GB were played without reading the whole file.

I want to play both .mp4 and .mkv files with Mi Video without reading the whole file at the beginning no matter the size of the file.

0

There are 0 answers