Как начать воспроизводить видео .mkv и .mp4 на Mi Video с помощью NanoHttpd без чтения всего файла (Android)

#android #http-headers #mp4 #mkv #nanohttpd

Вопрос:

Я использую библиотеку NanoHttpd для обслуживания видеофайлов типа .mkv и .mp4 в Mi Video.

Поскольку чтение входного потока было слишком медленным, я изменил размер буфера чтения NanoHttpd на 1 м:

 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;
        }
    }
}
 

Я изменил InputStream.skip() таким же образом:

 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;
}
 

Я переопределяю 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.