#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.