现如今网络上的视频大多数都是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)
效果:
如果我说的不够明白,欢迎提问,如果有错漏,欢迎指出文章来源:https://www.toymoban.com/news/detail-419670.html
来都来了,留个评论呗,点个赞呗文章来源地址https://www.toymoban.com/news/detail-419670.html
到了这里,关于安卓实现M3U8文件的下载和播放的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!