mirror of
https://github.com/zhigang1992/AndroidVideoCache.git
synced 2026-02-09 22:44:05 +08:00
108 lines
4.0 KiB
Java
108 lines
4.0 KiB
Java
package com.danikula.videocache;
|
|
|
|
import android.text.TextUtils;
|
|
|
|
import com.danikula.videocache.file.FileCache;
|
|
|
|
import java.io.BufferedOutputStream;
|
|
import java.io.IOException;
|
|
import java.io.OutputStream;
|
|
import java.net.Socket;
|
|
|
|
import static com.danikula.videocache.ProxyCacheUtils.DEFAULT_BUFFER_SIZE;
|
|
|
|
/**
|
|
* {@link ProxyCache} that read http url and writes data to {@link Socket}
|
|
*
|
|
* @author Alexey Danilov (danikula@gmail.com).
|
|
*/
|
|
class HttpProxyCache extends ProxyCache {
|
|
|
|
private static final float NO_CACHE_BARRIER = .2f;
|
|
|
|
private final HttpUrlSource source;
|
|
private final FileCache cache;
|
|
private CacheListener listener;
|
|
|
|
public HttpProxyCache(HttpUrlSource source, FileCache cache) {
|
|
super(source, cache);
|
|
this.cache = cache;
|
|
this.source = source;
|
|
}
|
|
|
|
public void registerCacheListener(CacheListener cacheListener) {
|
|
this.listener = cacheListener;
|
|
}
|
|
|
|
public void processRequest(GetRequest request, Socket socket) throws IOException, ProxyCacheException {
|
|
OutputStream out = new BufferedOutputStream(socket.getOutputStream());
|
|
String responseHeaders = newResponseHeaders(request);
|
|
out.write(responseHeaders.getBytes("UTF-8"));
|
|
|
|
long offset = request.rangeOffset;
|
|
if (isUseCache(request)) {
|
|
responseWithCache(out, offset);
|
|
} else {
|
|
responseWithoutCache(out, offset);
|
|
}
|
|
}
|
|
|
|
private boolean isUseCache(GetRequest request) throws ProxyCacheException {
|
|
int sourceLength = source.length();
|
|
boolean sourceLengthKnown = sourceLength > 0;
|
|
int cacheAvailable = cache.available();
|
|
// do not use cache for partial requests which too far from available cache. It seems user seek video.
|
|
return !sourceLengthKnown || !request.partial || request.rangeOffset <= cacheAvailable + sourceLength * NO_CACHE_BARRIER;
|
|
}
|
|
|
|
private String newResponseHeaders(GetRequest request) throws IOException, ProxyCacheException {
|
|
String mime = source.getMime();
|
|
boolean mimeKnown = !TextUtils.isEmpty(mime);
|
|
int length = cache.isCompleted() ? cache.available() : source.length();
|
|
boolean lengthKnown = length >= 0;
|
|
long contentLength = request.partial ? length - request.rangeOffset : length;
|
|
boolean addRange = lengthKnown && request.partial;
|
|
return new StringBuilder()
|
|
.append(request.partial ? "HTTP/1.1 206 PARTIAL CONTENT\n" : "HTTP/1.1 200 OK\n")
|
|
.append("Accept-Ranges: bytes\n")
|
|
.append(lengthKnown ? String.format("Content-Length: %d\n", contentLength) : "")
|
|
.append(addRange ? String.format("Content-Range: bytes %d-%d/%d\n", request.rangeOffset, length - 1, length) : "")
|
|
.append(mimeKnown ? String.format("Content-Type: %s\n", mime) : "")
|
|
.append("\n") // headers end
|
|
.toString();
|
|
}
|
|
|
|
private void responseWithCache(OutputStream out, long offset) throws ProxyCacheException, IOException {
|
|
byte[] buffer = new byte[DEFAULT_BUFFER_SIZE];
|
|
int readBytes;
|
|
while ((readBytes = read(buffer, offset, buffer.length)) != -1) {
|
|
out.write(buffer, 0, readBytes);
|
|
offset += readBytes;
|
|
}
|
|
out.flush();
|
|
}
|
|
|
|
private void responseWithoutCache(OutputStream out, long offset) throws ProxyCacheException, IOException {
|
|
HttpUrlSource newSourceNoCache = new HttpUrlSource(this.source);
|
|
try {
|
|
newSourceNoCache.open((int) offset);
|
|
byte[] buffer = new byte[DEFAULT_BUFFER_SIZE];
|
|
int readBytes;
|
|
while ((readBytes = newSourceNoCache.read(buffer)) != -1) {
|
|
out.write(buffer, 0, readBytes);
|
|
offset += readBytes;
|
|
}
|
|
out.flush();
|
|
} finally {
|
|
newSourceNoCache.close();
|
|
}
|
|
}
|
|
|
|
@Override
|
|
protected void onCachePercentsAvailableChanged(int percents) {
|
|
if (listener != null) {
|
|
listener.onCacheAvailable(cache.file, source.getUrl(), percents);
|
|
}
|
|
}
|
|
}
|