安卓实现M3U8文件的下载和播放

3 篇文章 0 订阅
2 篇文章 0 订阅

现如今网络上的视频大多数都是m3u8格式的,使用m3u8格式有以下好处

1. 方便切换码率,例如从高清转到蓝光

2. 节约流量,m3u8实际切割成一段段的TS后缀视频,传统请求是把整个文件流返回去,网络不好或者文件过大时,都会造成响应缓慢,m3u8则是返回一个个的ts文件,当前ts缓存完才会自动请求下一个ts,ts切割的很小,所以,几乎是秒响应

3 防盗,m3u8可以对ts文件加密,其他就不知道

下面介绍如何下载m3u8

本次介绍一个下载框架,aria官方文档  1.1 开始 · Aria 使用指南 (laoyuyu.me)

需要在app.build引入aria

implementation 'me.laoyuyu.aria:core:3.8.16'
annotationProcessor 'me.laoyuyu.aria:compiler:3.8.16'
implementation 'me.laoyuyu.aria:m3u8:3.8.16'

引入完成后,可以使用以下代码下载文件,,如果是mp4或者图片,可以使用以下代码下载,下载代码:

Aria.download(this).register();//注册aria

long taskId = Aria.download(SingleTaskActivity.this)
        .load(url) // 下载地址
        .setFilePath(filePath) // 设置文件保存路径
        .create();

如果是m3u8,需要重写IBandWidthUrlConverter和IVodTsUrlConverter,先说说m3u8,我们打开一个m3u8,发现他是一个索引文件,记录下一个m3u8的码率,下一个m3u8则记录对应的ts文件,如下

 但是有这样3三种情况,

1. 记录的码率文件是全路径路径,如:https://xxx.com/xxx/index.m3u8

2. 记录的码率是相对路径,如 xxx/index.m3u8(上图就是这种情况)

3. 记录的码率只要文件名,如 index.m3u8

那么aria就不知根据那种规则下载,所以我们要重写IBandWidthUrlConverter,同理Ts文件也一样,也要重写IVodTsUrlConverter

重写IBandWidthUrlConverter:

public class M3U8_Converter implements IBandWidthUrlConverter {
   @Override
   public String convert(String m3u8Url, String bandWidthUrl) {
       System.out.println("m3u8Url:"+m3u8Url);
       System.out.println("bandWidthUrl:"+bandWidthUrl);
       //m3u8Url:第一个m3u8,连接到下一个m3u8
       //bandWidthUrl:第二个m3u8,包含不同码率的ts视频
       /*第二个m3u8有些文件带有全路径,如:http://xxxx.xxx.com/xxx
       *有些没有,如:/20210927/3oCoCiM4/hls/index.m3u8
       *因此需要区分,区分规则如下:
       * 如果包含有http或者https,一定是全路径,直接访问
       * 如果没有则是用域名+第二个文件路径,如:(域名)http://xxxx.xxx.com/20210927/3oCoCiM4/hls/index.m3u8(路径)
       */
       if(bandWidthUrl.contains("http://")||bandWidthUrl.contains("https://")){
           return bandWidthUrl;
       }else{
           //获取域名
       URL url = new URL(m3u8Url);
       String host=url.getProtocol()+"://"+url.getHost()+":"+url.getDefaultPort();
       return host+"/"+bandWidthUrl;
       }

   }

重写IVodTsUrlConverter

public List<String> convert(String m3u8Url, List<String> tsUrls) {
    System.out.println("m3u8Url:"+m3u8Url);
    System.out.println("tsUrls:"+tsUrls);
    //m3u8Url:m3u8文件,包含ts文件
    //tsUrls:所有的ts文件
    /*某些ts文件带有全路径,如:http://xxxx.xxx.com/xxx.ts
     *有些没有只有相对路径,如:/20210927/3oCoCiM4/hls/xx.ts
     * 有些只有文件名,如:xxx.ts
     *因此需要区分,区分规则如下:
     * 如果包含有http或者https,一定是全路径,直接访问
     * 如果只有相对则是用域名+ts文件路径,如:(域名)http://xxxx.xxx.com/20210927/3oCoCiM4/hls/0.ts(路径)
     * 如果只有文件名,则是1 截取0到xxx.m3u8(不包含)的路径 2加上ts文件名,如:
     * http://xxxx.xxx.com/20210927/3oCoCiM4/index.m3u8
     * 去掉路径中xxx.m3u8的路径
     * http://xxxx.xxx.com/20210927/3oCoCiM4/
     * 加上ts文件名
     */
    List<String> newTslist=new ArrayList<>();
    String pattern="[0-9a-zA-Z]+[.]ts";
    Pattern r = Pattern.compile(pattern);
    for (int i = 0; i < tsUrls.size(); i++) {
        String tspath=tsUrls.get(i);
        Matcher m = r.matcher(tspath);
        //全路径
        if(tspath.contains("http://")||tspath.contains("https://")){
            newTslist.add(tspath);
        }
        //只有文件名
        else
        if(m.find()){
            int e=m3u8Url.lastIndexOf("/")+1;
            newTslist.add(m3u8Url.substring(0,e)+tspath);
        }
        //相对路径
        else{
            String host=Host.gethost(m3u8Url);
            newTslist.add(host+"/"+tspath);
        }
    }

    return newTslist;
}

重写完这两个类,我们启动下载时,就会根据我们重写的规则进行访问

启动下载

Aria.download(this).register();//注册aria
M3U8VodOption option = new M3U8VodOption(); // 创建m3u8点播文件配
option.setUseDefConvert(false);//必须设置false,否则不会使用你重写那两个类
option.setBandWidthUrlConverter(new M3U8_Converter());
option.setVodTsUrlConvert(new TS_Converter());
option.generateIndexFile();
long taskId = Aria.download(Activity_Home.context)
        .load("m3u8的下载路径") 
        .setFilePath("保存路径", true) // 设置点播文件保存路径,true表示,如果当前目录有文件,则覆盖相同的文件
        .m3u8VodOption(option)   // 调整下载模式为m3u8点播
        .create();

接下来是几个注解

//任务开始时

@Download.onTaskStart void taskStart(DownloadTask task) {}

//任务运行时

@Download.onTaskRunning void running(DownloadTask task) {}

//任务停止时 

@Download.onTaskStop void taskStop(DownloadTask task) {}

//下载失败时

@Download.onTaskFail void taskFail(DownloadTask task) {}

//下载完成时

@Download.onTaskComplete void taskComplete(DownloadTask task) {}

//任务删除时

@Download.onTaskCancel void taskCancel(DownloadTask task) {}

其中DownloadTask有以下方法

DownloadTask.getPercent();  //下载进度
DownloadTask.getSpeed();//下载速度,原始数据,需要手动转为mb/s,kb/s
DownloadTask.getConvertFileSize();//文件大小,已处理单位,自动为xxmb或者xxgb
DownloadTask.getConvertCurrentProgress();//已下载的文件大小,单位已处理

具体用法可以参考官方文档

这样m3u8就下载好了,我们可以在@Download.onTaskRunning void running(DownloadTask task) {}中监听下载进度等,具体可以根据实际业务开发

接下来我们讲讲m3u8的播放,网络m3u8可以直接赋值给videoview,也可以正常播放,我们理所当然的认为下载到本地的m3u8也可以赋值给videoview,然后正确的播放,但是,事实上,我们把本地路径赋值给videoview,不能正确播放,会出现该视频无法播放,那么怎么解决呢,m3u8格式只支持网络播放,那么我们把本地路径换为本地网络路径就可以解决,如http://127.0.0.1:80/index.m3u8,需要本地搭建webservice,搭建步骤如下:

引入nanohttpd

implementation 'org.nanohttpd:nanohttpd:2.3.1'

然后继承实现NanoHTTPD

public class MyServer extends NanoHTTPD {
   public static String M3U8="application/x-mpegURL";
   public static String Video="video/mp4";
   public static String text="video/mp4";
    public static String TS="video/mp2t";
    public MyServer(int port) {
        super(port);
    }

    public MyServer(int port,Context context) {
        super(port);
    }
    public MyServer(String hostname, int port) {
        super(hostname, port);
    }

    @Override
    public Response serve(IHTTPSession session) {

        try {
            //文件下载得保存路径
            String path = "/storage/emulated/0/Android/data/com.example.iflq/files/Download/";
            //本地地址转为网络地址
            if (session.getUri().contains(path)) {
                File f = new File(session.getUri());
                FileInputStream inputStream = new FileInputStream(f);
                return newFixedLengthResponse(Response.Status.OK, M3U8, inputStream,f.length());

            }

            //默认页面
            String msg = "<html><body><h1>Hello server</h1></body></html>";
            return newFixedLengthResponse(msg);
        }catch (Exception e){
            e.printStackTrace();
            String msg = "<html><body><h1>服务器出错</h1></body></html>";
            return newFixedLengthResponse(msg);
        }
    }
}

启动webservice

MyServer mMyServer = new MyServer(8080);
try {
    mMyServer.start();
} catch (IOException e) {
    e.printStackTrace();
}

这时候我们就可以把m3u8赋值给videoview了,videoView.setVideoPath("http://127.0.0.1:8080/index.m3u8");

注意文件路径(必须存在,否则一样不能播放),注意文件的返回格式(application/x-mpegURL)

效果:

如果我说的不够明白,欢迎提问,如果有错漏,欢迎指出

来都来了,留个评论呗,点个赞呗

  • 3
    点赞
  • 11
    收藏
    觉得还不错? 一键收藏
  • 3
    评论
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值