实现思路
红色框的 ProxyServer就是需要实现的一个代理服务器。 当客户端拿到一个视频的url(mp4或者m3u8)时,通过proxyServer转化为一个代理的url,然后请求代理服务器;代理服务器接收到客户端的请求后,先查看本地是否存在缓存,如果不存在则向真实服务器发送请求,拿到结果后再存到本地。
实现重点
缓存是一个代理服务器的主要部分,所以这部分是一个重点。本设计的缓存是一个分片的LRU缓存。分片的好处是灵活方便做LRU。当真实服务器返回一个大文件时,我们在进行切割后缓存在本地,并返回给客户端,不用等待所有数据返回后再返回给客户端。
使用方式
在app初始化的时候 创建代理服务器
public class APP extends Application { private static VideoCacheServer videoCacheServer; @Override public void onCreate() { super.onCreate(); if (videoCacheServer == null) { // 缓存路径 String cachePath = getCacheDir().getAbsolutePath(); // 缓存大小 1024 * 1024 * 500 videoCacheServer = new VideoCacheServer(cachePath, 1024 * 1024 * 500); } } public static VideoCacheServer getVideoProxyServer() { return videoCacheServer; } }
代理服务建立好了 ,使用的时候只需要将真实url转换为代理url就好了
String proxyUrl = APP.getVideoProxyServer().getLocalProxyUrl("https://sina.com-h-sina.com/20181024/21342_8f737b71/1000k/hls/index.m3u8"); videoView.setVideoPath(proxyUrl);
转换的规则即讲https的请求转换为http的请求 ,并且替换域名为代理服务器的地址,将真实服务器的地址作为参数添加到代理url的后面。
例如 sina.com-h-sina.com/20181024/21… 地址转换后变成了 https://127.0.0.1:3260/20181024/21342_8f737b71/1000k/hls/index.m3u8?RealHostParam=sina.com-h-sina.com 3260是VideoCacheServer监听的端口
实现细节
代理服务器的建立
public class VideoCacheServer{ private ExecutorService pool = Executors.newFixedThreadPool(20); public int start() { if (isRunning) { return curPort; } curPort = new Random().nextInt(65535); try { final ServerSocket server = new ServerSocket(curPort); isRunning = true; singleService.submit(new Runnable() { @Override public void run() { while (isRunning) { try { Socket connection = server.accept(); connection.setKeepAlive(true); pool.submit(new ProxyHandler(connection)); } catch (IOException ex) { if (Constant.enableLog) { logger.log(Level.WARNING, "Exception accepting connection", ex); } } catch (Exception ex) { if (Constant.enableLog) { logger.log(Level.SEVERE, "Unexpected error", ex); } } } } }); return curPort; } catch (IOException e) { e.printStackTrace(); return start(); } } }
通过socket实现端口的监听,当请求到来时,使用ProxyHandler来处理。
public class ProxyHandler implements Runnable { private Socket realClientSocket; ProxyHandler(Socket realClientSocket) { this.realClientSocket = realClientSocket; } @Override public void run() { try { BufferedOutputStream outputStream = new BufferedOutputStream(realClientSocket.getOutputStream()); BufferedInputStream inputStream = new BufferedInputStream(realClientSocket.getInputStream()); HttpRequest realRequest = HttpRequest.parse(inputStream); HttpResponse response = getResponseWithInterceptorChain(realRequest); writeResponseAndClose(response, outputStream); } catch (Exception e) { if (Constant.enableLog) { logger.log(Level.SEVERE, "error proxy ", e); } } finally { CloseUtil.close(realClientSocket); } } private HttpResponse getResponseWithInterceptorChain(HttpRequest realRequest) { List<Interceptor> interceptors = new ArrayList<>(); interceptors.add(new VideoTypeInterceptor()); interceptors.add(new HostFilterInterceptor(curPort)); interceptors.add(new CacheInterceptor(diskCache)); interceptors.add(new ConnectInterceptor()); InterceptorChain interceptorChain = new InterceptorChain(interceptors, realRequest, 0); return interceptorChain.proceed(realRequest); } }
ProxyHandler中使用拦截器的模式,将请求分部处理
VideoTypeInterceptor 将代理的url 还原为真实的url
CacheInterceptor 用于缓存
ConnectInterceptor 建立代理服务器与真实服务器的连接
VideoTypeInterceptor 主要是针对m3u8类型,因为m3u8会先返回一个m3u8的文件,文件里面记录了每个ts的地址,VideoTypeInterceptor就是将返回的文件中的ts地址转换为代理服务器的地址
项目地址 https://github.com/ZhangHao555/VideoCacheServerDemo
到此这篇关于Android 边播边缓存的实现(MP4 未加密m3u8)的文章就介绍到这了,更多相关Android 边播边缓存内容请搜索自由互联以前的文章或继续浏览下面的相关文章希望大家以后多多支持自由互联!