Android 边播边缓存的实现(MP4 未加密m3u8)

实现思路

红色框的 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 边播边缓存内容请搜索我们以前的文章或继续浏览下面的相关文章希望大家以后多多支持我们!

时间: 2020-11-08

浅谈Android LruCache的缓存策略

一.Android中的缓存策略 一般来说,缓存策略主要包含缓存的添加.获取和删除这三类操作.如何添加和获取缓存这个比较好理解,那么为什么还要删除缓存呢?这是因为不管是内存缓存还是硬盘缓存,它们的缓存大小都是有限的.当缓存满了之后,再想其添加缓存,这个时候就需要删除一些旧的缓存并添加新的缓存. 因此LRU(Least Recently Used)缓存算法便应运而生,LRU是近期最少使用的算法,它的核心思想是当缓存满时,会优先淘汰那些近期最少使用的缓存对象.采用LRU算法的缓存有两种:LrhCach

关于Android的 DiskLruCache磁盘缓存机制原理

目录 一.为什么用DiskLruCache 1.LruCache和DiskLruCache 2.为何使用DiskLruCache 二.DiskLruCache使用 1.添加依赖 2.创建DiskLruCache对象 3.添加 / 获取 缓存(一对一) 4.添加 / 获取 缓存(一对多) 三.源码分析 1.open() 2.rebuildJournal() 3.readJournal() 4.get() 5.validateKey 6.trimTOSize() 7.journalRebuildRe

android实现清理缓存功能

android之清理缓存实现,供大家参考,具体内容如下 一. 清理缓存首先要搞清楚清理哪些东西 1.app本身的功能比如录像,录音,更新都会产生文件,需要清理 2.app的默认缓存地址cache 二. 搞清楚要清理的文件夹位置 1.首先app自身的功能就要看自己把它放在了什么位置 2.默认缓存地址:getActivity().getExternalCacheDir(); 这个位置是在storage/emulated/0/Android/data/com.xxxxxapp/cache 三.代码功能

Android实现清除应用缓存功能

本文实例为大家分享了Android清除应用缓存的具体代码,供大家参考,具体内容如下 import android.content.Context; import android.os.Environment; import java.io.File; import java.math.BigDecimal; /** * 获取缓存大小并清理缓存 */ public class DataCleanManagerUtils { /** * Context.getExternalFilesDir() -

Android Studio缓存文件夹配置教程

安装完,或者绿色版解压完,先别打开Android Stduio.要先配置下Android Studio 的缓存路径. 这个缓存文件主要是存放一些AndroidStudio设置和插件和项目的缓存信息的. 我用的是AS的老版本,缓存文件夹如图.默认是放在C盘系统盘里面的,这里是没改直接打开的,实际上安装完应该先别打开修改完再打开! 打开会看到 为什么Android Stduio启动速度比Eclipse快,也是托这个缓存文件夹的关系.但缺点是第一次建立缓存会比较慢. 为什么要配置这个文件呢? 因为这个

Android缓存机制——LruCache的详解

概述 LruCache的核心原理就是对LinkedHashMap的有效利用,它的内部存在一个LinkedHashMap成员变量,值得注意的4个方法:构造方法.get.put.trimToSize LRU(Least Recently Used)缓存算法便应运而生,LRU是最近最少使用的算法,它的核心思想是当缓存满时,会优先淘汰那些最近最少使用的缓存对象.采用LRU算法的缓存有两种:LrhCache和DisLruCache,分别用于实现内存缓存和硬盘缓存,其核心思想都是LRU缓存算法. LRU原理

Android缓存之DiskLruCache磁盘缓存的使用

DiskLruCache和LruCache不同的是,LruCache是内存缓存,而DiskLruCache是指磁盘缓存,顾名思义就是把文件缓存到磁盘,也也就是手机的内存卡中.接下来先简单介绍DiskLruCache的使用方法. 下载源码 DiskLruCache并没有在 SDK中存在,但又是谷歌提倡的.所以我们要先把DiskLruCache的源码下载下来. 我们可以通过下面这个地址下载源码:https://github.com/JakeWharton/DiskLruCache/tree/mast

Android 获取应用缓存大小与清除缓存的方法

如下所示: package com.lucasey.littleant.frame; /** * 文 件 名: FileCacheUtils.java * 描 述: 主要功能有清除内/外缓存,清除数据库,清除sharedPreference,清除files和清除自定义目录 * */ import java.io.File; import java.math.BigDecimal; import android.content.Context; import android.os.Environm

IOS获取缓存文件的大小并清除缓存文件的方法

移动应用在处理网络资源时,一般都会做离线缓存处理,其中以图片缓存最为典型,其中很流行的离线缓存框架为SDWebImage. 但是,离线缓存会占用手机存储空间,所以缓存清理功能基本成为资讯.购物.阅读类app的标配功能. 今天介绍的离线缓存功能的实现,主要分为缓存文件大小的获取.清除缓存文件的实现. 1. 获取缓存文件的大小 -( float )readCacheSize { NSString *cachePath = [NSSearchPathForDirectoriesInDomains (N

Android中Glide获取缓存大小并清除缓存图片

清除Glide缓存 Glide自带清除缓存的功能,分别对应Glide.get(context).clearDiskCache();(清除磁盘缓存)与Glide.get(context).clearMemory();(清除内存缓存)两个方法.其中clearDiskCache()方法必须运行在子线程,clearMemory()方法必须运行在主线程,这是这两个方法所强制要求的,详见源码. 获取Glide缓存空间大小 这个网上也有过一些介绍,但是给出的实现代码存在一些问题,我这里做了一定的修改.一下方法

Android获取应用程序大小和缓存的实例代码

info package com.qin.appsize; import android.content.Intent; import android.graphics.drawable.Drawable; //Model类 ,用来存储应用程序信息 public class AppInfo { private String appLabel; //应用程序标签 private Drawable appIcon ; //应用程序图像 private Intent intent ; //启动应用程序

iOS开发之1行代码实现缓存计算及清除缓存

话不多说,直接撸代码 // // gzhCache.h // cache // // Created by 郭志贺 on 2020/5/27. // Copyright © 2020 郭志贺. All rights reserved. // #import <Foundation/Foundation.h> NS_ASSUME_NONNULL_BEGIN @interface gzhCache : NSObject /// 计算缓存大小 +(float)filePath; /// 清理缓存 +

Android获取SD卡路径及SDCard内存的方法

本文实例讲述了Android获取SD卡路径及SDCard内存的方法.分享给大家供大家参考.具体分析如下: 昨天在研究拍照后突破的存储路径的问题,开始存储路径写死为: private String folder = "/sdcard/DCIM/Camera/"(SD卡上拍照程序的图片存储路径); 后来发现这样写虽然一般不会出错,但不是很好,因为不同相机,可能路径会出问题.较好的方法是通过Environment 来获取路径,最后给出一个例子,教你怎样获取SDCard 的内存,显示出来告诉用

android 获取手机内存及 内存可用空间的方法

实例如下: //1.获取内存可用大小,内存路径 String path=Environment.getDataDirectory().getAbsolutePath(); String memoryAvaliSpace= Formatter.formatFileSize(this,getAvailSpace(path)); //2.获取sd卡可用大小,sd卡路径 String sdPath=Environment.getExternalStorageDirectory().getAbsolute

Android获取和读取短信验证码的实现方法

现如今,验证码在Android的客户端还是非常普遍的.通过手机账号和验证码直接去注册应用账户的信息.很多应用都以这种方式来完成注册.简单的介绍一下吧. Android获取短信验证码还是比较简单的,通过Mob官网提供的ShareSDK,调用其中内部的方法,就可以获取到短信的验证码了.提供一下Mob的官网地址.http://www.mob.com/#/在官网上注册相关的信息之后,下载相关的jar包和.so文件就可以实现获取短信验证码了(2.0之前的版本都需要下载jar包和 .so文件,而现在的2.2

Android获取手机SIM卡运营商信息的方法

本文实例讲述了Android获取手机SIM卡运营商信息的方法,对于Android程序设计有非常实用的价值.分享给大家供大家参考之用.具体方法如下: 主要功能代码如下: /** * 获取SIM卡运营商 * * @param context * @return */ public static String getOperators(Context context) { TelephonyManager tm = (TelephonyManager) context .getSystemServic

Android获取屏幕或View宽度和高度的方法

本文实例讲述了Android获取屏幕或View宽度和高度的方法.分享给大家供大家参考,具体如下: 在Activity中获取屏幕的高度和宽度 Display display=getWindowManager().getDefaultDisplay(); int width=display.getWidth(); int height=display.getHeight(); 在重写ViewGroup中获取屏幕的有效宽度和高度在OnMesure方法中 protected void onMeasure