From b670e3e3838d60272a5064a6cd33468f7d5fa231 Mon Sep 17 00:00:00 2001 From: laoyuyu <511455842@qq.com> Date: Thu, 6 Jun 2019 21:19:30 +0800 Subject: [PATCH] =?UTF-8?q?hls=20=E7=9B=B4=E6=92=AD=E4=B8=8B=E8=BD=BD?= =?UTF-8?q?=E5=AE=9E=E7=8E=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../arialyy/aria/core/common/AbsFileer.java | 4 - .../aria/core/common/AbsThreadTask.java | 4 +- .../aria/core/common/BaseListener.java | 2 +- .../com/arialyy/aria/core/common/IUtil.java | 5 - .../aria/core/common/RecordHandler.java | 65 ++-- .../arialyy/aria/core/common/TaskRecord.java | 5 +- .../aria/core/common/ThreadRecord.java | 6 +- .../core/download/DNormalConfigHandler.java | 37 ++- .../aria/core/download/DownloadTask.java | 11 +- .../downloader/SimpleDownloadUtil.java | 4 - .../core/download/group/AbsGroupUtil.java | 4 - .../download/group/SubDownloadLoader.java | 4 - .../core/download/m3u8/BaseM3U8Loader.java | 52 ++++ .../download/m3u8/ILiveTsUrlConverter.java | 32 ++ .../core/download/m3u8/ITsMergeHandler.java | 4 +- ...Converter.java => IVodTsUrlConverter.java} | 6 +- .../aria/core/download/m3u8/M3U8Delegate.java | 15 +- ...ileInfoThread.java => M3U8InfoThread.java} | 118 +++++++- .../{LiveProtocol.java => M3U8KeyInfo.java} | 33 +- .../core/download/m3u8/M3U8LiveDelegate.java | 17 +- .../download/m3u8/M3U8LiveDownloadUtil.java | 179 +++++++++++ .../core/download/m3u8/M3U8LiveLoader.java | 285 ++++++++++++++++++ .../core/download/m3u8/M3U8TaskConfig.java | 37 ++- ...FileUtil.java => M3U8VodDownloadUtil.java} | 82 +++-- ...M3U8FileLoader.java => M3U8VodLoader.java} | 40 +-- .../arialyy/aria/core/inf/ITaskWrapper.java | 4 +- .../upload/uploader/SimpleUploadUtil.java | 4 - .../java/com/arialyy/aria/orm/DBConfig.java | 2 +- .../com/arialyy/aria/util/RecordUtil.java | 8 +- app/src/main/AndroidManifest.xml | 3 +- .../java/com/arialyy/simple/MainActivity.java | 9 +- .../m3u8/M3U8LiveDownloadActivity.java | 270 +++++++++++++++++ .../core/download/m3u8/M3U8LiveModule.java | 95 ++++++ ...vity.java => M3U8VodDownloadActivity.java} | 31 +- .../{M3U8Module.java => M3U8VodModule.java} | 10 +- .../arialyy/simple/modlue/CommonModule.java | 3 +- app/src/main/res/drawable/ic_live.xml | 12 + .../main/res/layout/activity_m3u8_live.xml | 112 +++++++ ...ctivity_m3u8.xml => activity_m3u8_vod.xml} | 2 +- app/src/main/res/values/strings.xml | 3 + 40 files changed, 1378 insertions(+), 241 deletions(-) create mode 100644 Aria/src/main/java/com/arialyy/aria/core/download/m3u8/BaseM3U8Loader.java create mode 100644 Aria/src/main/java/com/arialyy/aria/core/download/m3u8/ILiveTsUrlConverter.java rename Aria/src/main/java/com/arialyy/aria/core/download/m3u8/{ITsUrlConverter.java => IVodTsUrlConverter.java} (84%) rename Aria/src/main/java/com/arialyy/aria/core/download/m3u8/{M3U8FileInfoThread.java => M3U8InfoThread.java} (69%) rename Aria/src/main/java/com/arialyy/aria/core/download/m3u8/{LiveProtocol.java => M3U8KeyInfo.java} (70%) create mode 100644 Aria/src/main/java/com/arialyy/aria/core/download/m3u8/M3U8LiveDownloadUtil.java create mode 100644 Aria/src/main/java/com/arialyy/aria/core/download/m3u8/M3U8LiveLoader.java rename Aria/src/main/java/com/arialyy/aria/core/download/m3u8/{M3U8FileUtil.java => M3U8VodDownloadUtil.java} (58%) rename Aria/src/main/java/com/arialyy/aria/core/download/m3u8/{M3U8FileLoader.java => M3U8VodLoader.java} (89%) create mode 100644 app/src/main/java/com/arialyy/simple/core/download/m3u8/M3U8LiveDownloadActivity.java create mode 100644 app/src/main/java/com/arialyy/simple/core/download/m3u8/M3U8LiveModule.java rename app/src/main/java/com/arialyy/simple/core/download/m3u8/{M3U8DownloadActivity.java => M3U8VodDownloadActivity.java} (90%) rename app/src/main/java/com/arialyy/simple/core/download/m3u8/{M3U8Module.java => M3U8VodModule.java} (92%) create mode 100644 app/src/main/res/drawable/ic_live.xml create mode 100644 app/src/main/res/layout/activity_m3u8_live.xml rename app/src/main/res/layout/{activity_m3u8.xml => activity_m3u8_vod.xml} (96%) diff --git a/Aria/src/main/java/com/arialyy/aria/core/common/AbsFileer.java b/Aria/src/main/java/com/arialyy/aria/core/common/AbsFileer.java index 0495d62c..b98b8786 100644 --- a/Aria/src/main/java/com/arialyy/aria/core/common/AbsFileer.java +++ b/Aria/src/main/java/com/arialyy/aria/core/common/AbsFileer.java @@ -252,10 +252,6 @@ public abstract class AbsFileer(); record.threadNum = threadNum; - // 处理分块和动态文件参数 - if (getRecordType() == TYPE_DOWNLOAD) { - record.isBlock = threadNum > 1 && Configuration.getInstance().downloadCfg.isUseBlock(); - // 线程数为1,或者使用了分块,则认为是使用动态长度文件 - record.isOpenDynamicFile = threadNum == 1 || record.isBlock; - } else { - record.isBlock = false; - } - if (mTaskWrapper.getRequestType() == ITaskWrapper.M3U8_FILE) { - record.taskType = TaskRecord.TYPE_M3U8; + int requestType = mTaskWrapper.getRequestType(); + if (requestType == ITaskWrapper.M3U8_VOD) { + record.taskType = TaskRecord.TYPE_M3U8_VOD; + record.isOpenDynamicFile = true; + } else if (requestType == ITaskWrapper.M3U8_LIVE) { + record.taskType = TaskRecord.TYPE_M3U8_LIVE; record.isOpenDynamicFile = true; } else { + if (getRecordType() == TYPE_DOWNLOAD) { + record.isBlock = threadNum > 1 && Configuration.getInstance().downloadCfg.isUseBlock(); + // 线程数为1,或者使用了分块,则认为是使用动态长度文件 + record.isOpenDynamicFile = threadNum == 1 || record.isBlock; + } else { + record.isBlock = false; + } record.taskType = TaskRecord.TYPE_HTTP_FTP; - } - record.isGroupRecord = mEntity.isGroupChild(); - if (record.isGroupRecord) { - if (mEntity instanceof DownloadEntity) { - record.dGroupHash = ((DownloadEntity) mEntity).getGroupHash(); + record.isGroupRecord = mEntity.isGroupChild(); + if (record.isGroupRecord) { + if (mEntity instanceof DownloadEntity) { + record.dGroupHash = ((DownloadEntity) mEntity).getGroupHash(); + } } } + return record; } @@ -369,7 +379,9 @@ public class RecordHandler { private void saveRecord() { mRecord.threadNum = mRecord.threadRecords.size(); mRecord.save(); - DbEntity.saveAll(mRecord.threadRecords); + if (mRecord.threadRecords != null && !mRecord.threadRecords.isEmpty()) { + DbEntity.saveAll(mRecord.threadRecords); + } ALog.d(TAG, String.format("保存记录,线程记录数:%s", mRecord.threadRecords.size())); } @@ -405,9 +417,12 @@ public class RecordHandler { */ private int getNewTaskThreadNum() { if (getRecordType() == TYPE_DOWNLOAD) { - if (mTaskWrapper.getRequestType() == ITaskWrapper.M3U8_FILE) { + if (mTaskWrapper.getRequestType() == ITaskWrapper.M3U8_VOD) { return ((DTaskWrapper) mTaskWrapper).asM3U8().getUrls().size(); } + if (mTaskWrapper.getRequestType() == ITaskWrapper.M3U8_LIVE) { + return 1; + } if (!mTaskWrapper.isSupportBP() || mTaskWrapper.asHttp().isChunked()) { return 1; } diff --git a/Aria/src/main/java/com/arialyy/aria/core/common/TaskRecord.java b/Aria/src/main/java/com/arialyy/aria/core/common/TaskRecord.java index e1956d58..5dbd8b41 100644 --- a/Aria/src/main/java/com/arialyy/aria/core/common/TaskRecord.java +++ b/Aria/src/main/java/com/arialyy/aria/core/common/TaskRecord.java @@ -30,7 +30,8 @@ import java.util.List; */ public class TaskRecord extends DbEntity { public static final int TYPE_HTTP_FTP = 0; - public static final int TYPE_M3U8 = 1; + public static final int TYPE_M3U8_VOD = 1; + public static final int TYPE_M3U8_LIVE = 2; @Ignore public List threadRecords; @@ -89,7 +90,7 @@ public class TaskRecord extends DbEntity { /** * 线程类型 - * {@link #TYPE_HTTP_FTP}、{@link #TYPE_M3U8} + * {@link #TYPE_HTTP_FTP}、{@link #TYPE_M3U8_VOD} */ public int taskType = 0; } diff --git a/Aria/src/main/java/com/arialyy/aria/core/common/ThreadRecord.java b/Aria/src/main/java/com/arialyy/aria/core/common/ThreadRecord.java index a6d1f0b2..41186d45 100644 --- a/Aria/src/main/java/com/arialyy/aria/core/common/ThreadRecord.java +++ b/Aria/src/main/java/com/arialyy/aria/core/common/ThreadRecord.java @@ -56,12 +56,12 @@ public class ThreadRecord extends DbEntity { /** * 线程类型 - * {@link TaskRecord#TYPE_HTTP_FTP}、{@link TaskRecord#TYPE_M3U8} + * {@link TaskRecord#TYPE_HTTP_FTP}、{@link TaskRecord#TYPE_M3U8_VOD} */ public int threadType = 0; /** - * m3u8文件的下载地址 + * ts文件的下载地址 */ - public String m3u8url; + public String tsUrl; } diff --git a/Aria/src/main/java/com/arialyy/aria/core/download/DNormalConfigHandler.java b/Aria/src/main/java/com/arialyy/aria/core/download/DNormalConfigHandler.java index 1b790fa4..be920abc 100644 --- a/Aria/src/main/java/com/arialyy/aria/core/download/DNormalConfigHandler.java +++ b/Aria/src/main/java/com/arialyy/aria/core/download/DNormalConfigHandler.java @@ -102,23 +102,29 @@ class DNormalConfigHandler implements IConfigHandler if (b) { mEntity.save(); } - if (mTarget.getTaskWrapper().getRequestType() == ITaskWrapper.M3U8_FILE) { - checkM3u8(); + if (mTarget.getTaskWrapper().getRequestType() == ITaskWrapper.M3U8_VOD + || mTarget.getTaskWrapper().getRequestType() == ITaskWrapper.M3U8_LIVE) { + checkM3U8(); } return b; } - private void checkM3u8() { + private void checkM3U8() { File file = new File(mTempFilePath); DTaskWrapper wrapper = (DTaskWrapper) mTarget.getTaskWrapper(); - if (wrapper.getRequestType() == ITaskWrapper.M3U8_FILE) { - // 缓存文件夹格式:问文件夹/.文件名_码率 - wrapper.asM3U8() - .setCacheDir(String.format("%s/.%s_%s", file.getPath(), file.getName(), - wrapper.asM3U8().getBandWidth())); - } - if (mEntity.getFileSize() == 0) { - ALog.w(TAG, "由于m3u8协议的特殊性质,无法获取到正确到文件长度,因此你需要自行设置文件大小:asM3U8().setFileSize(xxx)"); + // 缓存文件夹格式:问文件夹/.文件名_码率 + wrapper.asM3U8() + .setCacheDir(String.format("%s/.%s_%s", file.getParent(), file.getName(), + wrapper.asM3U8().getBandWidth())); + if (wrapper.getRequestType() == ITaskWrapper.M3U8_VOD) { + if (mEntity.getFileSize() == 0) { + ALog.w(TAG, "由于m3u8协议的特殊性质,无法获取到正确到文件长度,因此你需要自行设置文件大小:asM3U8().setFileSize(xxx)"); + } + } else if (wrapper.getRequestType() == ITaskWrapper.M3U8_LIVE) { + if (file.exists()) { + ALog.w(TAG, "对于直播来说,每次下载都是一个新文件,所以你需要设置新都文件路径,否则Aria框架将会覆盖已下载的文件"); + file.delete(); + } } if (wrapper.asM3U8().getBandWidthUrlConverter() != null @@ -139,7 +145,7 @@ class DNormalConfigHandler implements IConfigHandler File file = new File(filePath); if (file.isDirectory()) { if (mTarget.getTargetType() == ITargetHandler.D_HTTP - || mTarget.getTaskWrapper().getRequestType() == ITaskWrapper.M3U8_FILE) { + || mTarget.getTaskWrapper().getRequestType() == ITaskWrapper.M3U8_VOD) { ALog.e(TAG, String.format("下载失败,保存路径【%s】不能为文件夹,路径需要是完整的文件路径,如:/mnt/sdcard/game.zip", filePath)); return false; @@ -164,16 +170,17 @@ class DNormalConfigHandler implements IConfigHandler ALog.w(TAG, String.format("保存路径【%s】已经被其它任务占用,当前任务将覆盖该路径的文件", filePath)); RecordUtil.delTaskRecord(filePath, RecordHandler.TYPE_DOWNLOAD); mTarget.setTaskWrapper( - TaskWrapperManager.getInstance() - .getHttpTaskWrapper(DTaskWrapper.class, mUrl)); + TaskWrapperManager.getInstance().getHttpTaskWrapper(DTaskWrapper.class, mUrl)); } } File oldFile = new File(mEntity.getFilePath()); File newFile = new File(filePath); mEntity.setFilePath(filePath); mEntity.setFileName(newFile.getName()); + // 如过使用Content-Disposition中的文件名,将不会执行重命名工作 - if (mTarget.getTaskWrapper().asHttp().isUseServerFileName()) { + if (mTarget.getTaskWrapper().asHttp().isUseServerFileName() + || mTarget.getTaskWrapper().getRequestType() == ITaskWrapper.M3U8_LIVE) { return true; } if (oldFile.exists()) { diff --git a/Aria/src/main/java/com/arialyy/aria/core/download/DownloadTask.java b/Aria/src/main/java/com/arialyy/aria/core/download/DownloadTask.java index a6aadfb2..a213dd15 100644 --- a/Aria/src/main/java/com/arialyy/aria/core/download/DownloadTask.java +++ b/Aria/src/main/java/com/arialyy/aria/core/download/DownloadTask.java @@ -21,7 +21,8 @@ import android.os.Looper; import com.arialyy.aria.core.AriaManager; import com.arialyy.aria.core.common.IUtil; import com.arialyy.aria.core.download.downloader.SimpleDownloadUtil; -import com.arialyy.aria.core.download.m3u8.M3U8FileUtil; +import com.arialyy.aria.core.download.m3u8.M3U8LiveDownloadUtil; +import com.arialyy.aria.core.download.m3u8.M3U8VodDownloadUtil; import com.arialyy.aria.core.inf.AbsNormalTask; import com.arialyy.aria.core.inf.IDownloadListener; import com.arialyy.aria.core.inf.ITaskWrapper; @@ -89,10 +90,12 @@ public class DownloadTask extends AbsNormalTask { } @Override protected synchronized IUtil createUtil() { - if (mTaskWrapper.getRequestType() == ITaskWrapper.D_HTTP){ + if (mTaskWrapper.getRequestType() == ITaskWrapper.M3U8_VOD) { + return new M3U8VodDownloadUtil(mTaskWrapper, (IDownloadListener) mListener); + } else if (mTaskWrapper.getRequestType() == ITaskWrapper.M3U8_LIVE) { + return new M3U8LiveDownloadUtil(mTaskWrapper, (IDownloadListener) mListener); + } else { return new SimpleDownloadUtil(mTaskWrapper, (IDownloadListener) mListener); - }else { - return new M3U8FileUtil(mTaskWrapper, (IDownloadListener) mListener); } } diff --git a/Aria/src/main/java/com/arialyy/aria/core/download/downloader/SimpleDownloadUtil.java b/Aria/src/main/java/com/arialyy/aria/core/download/downloader/SimpleDownloadUtil.java index 31b1f2d1..8d85df07 100644 --- a/Aria/src/main/java/com/arialyy/aria/core/download/downloader/SimpleDownloadUtil.java +++ b/Aria/src/main/java/com/arialyy/aria/core/download/downloader/SimpleDownloadUtil.java @@ -97,10 +97,6 @@ public class SimpleDownloadUtil implements IUtil { new Thread(createInfoThread()).start(); } - @Override public void resume() { - start(); - } - @Override public void setMaxSpeed(int speed) { mDownloader.setMaxSpeed(speed); } diff --git a/Aria/src/main/java/com/arialyy/aria/core/download/group/AbsGroupUtil.java b/Aria/src/main/java/com/arialyy/aria/core/download/group/AbsGroupUtil.java index 1f537948..d817207c 100644 --- a/Aria/src/main/java/com/arialyy/aria/core/download/group/AbsGroupUtil.java +++ b/Aria/src/main/java/com/arialyy/aria/core/download/group/AbsGroupUtil.java @@ -220,10 +220,6 @@ public abstract class AbsGroupUtil implements IUtil, Runnable { } - @Override public void resume() { - start(); - } - @Override public void setMaxSpeed(int speed) { Set keys = mSubQueue.getExec().keySet(); for (String key : keys) { diff --git a/Aria/src/main/java/com/arialyy/aria/core/download/group/SubDownloadLoader.java b/Aria/src/main/java/com/arialyy/aria/core/download/group/SubDownloadLoader.java index 0ac17ef0..ff5b4769 100644 --- a/Aria/src/main/java/com/arialyy/aria/core/download/group/SubDownloadLoader.java +++ b/Aria/src/main/java/com/arialyy/aria/core/download/group/SubDownloadLoader.java @@ -128,10 +128,6 @@ class SubDownloadLoader implements IUtil { } } - @Override public void resume() { - start(); - } - @Override public void setMaxSpeed(int speed) { if (mDownloader != null) { mDownloader.setMaxSpeed(speed); diff --git a/Aria/src/main/java/com/arialyy/aria/core/download/m3u8/BaseM3U8Loader.java b/Aria/src/main/java/com/arialyy/aria/core/download/m3u8/BaseM3U8Loader.java new file mode 100644 index 00000000..f6099b67 --- /dev/null +++ b/Aria/src/main/java/com/arialyy/aria/core/download/m3u8/BaseM3U8Loader.java @@ -0,0 +1,52 @@ +/* + * Copyright (C) 2016 AriaLyy(https://github.com/AriaLyy/Aria) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.arialyy.aria.core.download.m3u8; + +import com.arialyy.aria.core.common.AbsFileer; +import com.arialyy.aria.core.download.DTaskWrapper; +import com.arialyy.aria.core.download.DownloadEntity; +import com.arialyy.aria.core.inf.IEventListener; +import com.arialyy.aria.util.CommonUtil; +import java.io.File; + +public abstract class BaseM3U8Loader extends AbsFileer { + + BaseM3U8Loader(IEventListener listener, DTaskWrapper wrapper) { + super(listener, wrapper); + } + + @Override protected long delayTimer() { + return 2000; + } + + /** + * 获取ts文件保存路径 + * + * @param dirCache 缓存目录 + * @param threadId ts文件名 + */ + public static String getTsFilePath(String dirCache, int threadId) { + return String.format("%s/%s.ts", dirCache, threadId); + } + + String getCacheDir() { + String cacheDir = mTaskWrapper.asM3U8().getCacheDir(); + if (!new File(cacheDir).exists()) { + CommonUtil.createDir(cacheDir); + } + return cacheDir; + } +} diff --git a/Aria/src/main/java/com/arialyy/aria/core/download/m3u8/ILiveTsUrlConverter.java b/Aria/src/main/java/com/arialyy/aria/core/download/m3u8/ILiveTsUrlConverter.java new file mode 100644 index 00000000..9618794b --- /dev/null +++ b/Aria/src/main/java/com/arialyy/aria/core/download/m3u8/ILiveTsUrlConverter.java @@ -0,0 +1,32 @@ +/* + * Copyright (C) 2016 AriaLyy(https://github.com/AriaLyy/Aria) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.arialyy.aria.core.download.m3u8; + +/** + * M3U8 直播下载,ts url转换器,对于某些服务器,返回的ts地址可以是相对地址,也可能是处理过的 + * 对于这种情况,你需要使用url转换器将地址转换为可正常访问的http地址 + */ +public interface ILiveTsUrlConverter { + + /** + * 处理#EXTINF信息,对于某些服务器,返回的切片信息有可能是相对地址,因此,你需要自行转换为可下载http连接 + * + * @param m3u8Url m3u8文件下载地址 + * @param tsUrl ts文件下载地址 + * @return 转换后的http地址 + */ + String convert(String m3u8Url, String tsUrl); +} diff --git a/Aria/src/main/java/com/arialyy/aria/core/download/m3u8/ITsMergeHandler.java b/Aria/src/main/java/com/arialyy/aria/core/download/m3u8/ITsMergeHandler.java index 357e5f79..0e30828e 100644 --- a/Aria/src/main/java/com/arialyy/aria/core/download/m3u8/ITsMergeHandler.java +++ b/Aria/src/main/java/com/arialyy/aria/core/download/m3u8/ITsMergeHandler.java @@ -15,6 +15,7 @@ */ package com.arialyy.aria.core.download.m3u8; +import android.support.annotation.Nullable; import java.util.List; /** @@ -25,8 +26,9 @@ public interface ITsMergeHandler { /** * 合并ts文件 * + * @param keyInfo ts文件加密信息,如果ts文件不是加密的,该字段为空 * @param tsPath ts文件列表 * @return {@code true} 合并成功 */ - boolean merge(List tsPath); + boolean merge(@Nullable M3U8KeyInfo keyInfo, List tsPath); } diff --git a/Aria/src/main/java/com/arialyy/aria/core/download/m3u8/ITsUrlConverter.java b/Aria/src/main/java/com/arialyy/aria/core/download/m3u8/IVodTsUrlConverter.java similarity index 84% rename from Aria/src/main/java/com/arialyy/aria/core/download/m3u8/ITsUrlConverter.java rename to Aria/src/main/java/com/arialyy/aria/core/download/m3u8/IVodTsUrlConverter.java index f073e5f8..2ccfc7a6 100644 --- a/Aria/src/main/java/com/arialyy/aria/core/download/m3u8/ITsUrlConverter.java +++ b/Aria/src/main/java/com/arialyy/aria/core/download/m3u8/IVodTsUrlConverter.java @@ -18,16 +18,16 @@ package com.arialyy.aria.core.download.m3u8; import java.util.List; /** - * M3U8 ts 文件url转换器,对于某些服务器,返回的ts地址可以是相对地址,也可能是处理过的 + * M3U8 点播文件下载,ts url转换器,对于某些服务器,返回的ts地址可以是相对地址,也可能是处理过的 * 对于这种情况,你需要使用url转换器将地址转换为可正常访问的http地址 */ -public interface ITsUrlConverter { +public interface IVodTsUrlConverter { /** * 处理#EXTINF信息,对于某些服务器,返回的切片信息有可能是相对地址,因此,你需要自行转换为可下载http连接 * * @param m3u8Url m3u8文件下载地址 - * @param tsUrls ts文件下载地址 + * @param tsUrls ts文件下载地址列表 * @return 根据切片信息转换后的http连接列表,如果你的切片信息是可以直接下载的http连接,直接返回extInf便可 */ List convert(String m3u8Url, List tsUrls); diff --git a/Aria/src/main/java/com/arialyy/aria/core/download/m3u8/M3U8Delegate.java b/Aria/src/main/java/com/arialyy/aria/core/download/m3u8/M3U8Delegate.java index 69f12afc..ce930251 100644 --- a/Aria/src/main/java/com/arialyy/aria/core/download/m3u8/M3U8Delegate.java +++ b/Aria/src/main/java/com/arialyy/aria/core/download/m3u8/M3U8Delegate.java @@ -30,7 +30,7 @@ public class M3U8Delegate extends BaseDelegate public M3U8Delegate(TARGET target) { super(target); mTaskWrapper = (DTaskWrapper) mTarget.getTaskWrapper(); - mTaskWrapper.setRequestType(AbsTaskWrapper.M3U8_FILE); + mTaskWrapper.setRequestType(AbsTaskWrapper.M3U8_VOD); } /** @@ -70,10 +70,10 @@ public class M3U8Delegate extends BaseDelegate * M3U8 ts 文件url转换器,对于某些服务器,返回的ts地址可以是相对地址,也可能是处理过的 * 对于这种情况,你需要使用url转换器将地址转换为可正常访问的http地址 * - * @param converter {@link ITsUrlConverter} + * @param converter {@link IVodTsUrlConverter} */ - public M3U8Delegate setTsUrlConvert(ITsUrlConverter converter) { - mTaskWrapper.asM3U8().setTsUrlConverter(converter); + public M3U8Delegate setTsUrlConvert(IVodTsUrlConverter converter) { + mTaskWrapper.asM3U8().setVodUrlConverter(converter); return this; } @@ -104,11 +104,4 @@ public class M3U8Delegate extends BaseDelegate public M3U8LiveDelegate asLive() { return new M3U8LiveDelegate<>(mTarget); } - - ///** - // * 处理需要解码的ts文件 - // */ - //public M3U8Delegate setDecodeAdapter() { - // return this; - //} } diff --git a/Aria/src/main/java/com/arialyy/aria/core/download/m3u8/M3U8FileInfoThread.java b/Aria/src/main/java/com/arialyy/aria/core/download/m3u8/M3U8InfoThread.java similarity index 69% rename from Aria/src/main/java/com/arialyy/aria/core/download/m3u8/M3U8FileInfoThread.java rename to Aria/src/main/java/com/arialyy/aria/core/download/m3u8/M3U8InfoThread.java index 2a63f62f..d254381d 100644 --- a/Aria/src/main/java/com/arialyy/aria/core/download/m3u8/M3U8FileInfoThread.java +++ b/Aria/src/main/java/com/arialyy/aria/core/download/m3u8/M3U8InfoThread.java @@ -25,13 +25,18 @@ import com.arialyy.aria.core.common.http.HttpTaskConfig; import com.arialyy.aria.core.download.DTaskWrapper; import com.arialyy.aria.core.download.DownloadEntity; import com.arialyy.aria.core.download.downloader.ConnectionHelp; +import com.arialyy.aria.core.inf.ITaskWrapper; import com.arialyy.aria.exception.M3U8Exception; import com.arialyy.aria.exception.TaskException; import com.arialyy.aria.util.ALog; import com.arialyy.aria.util.CheckUtil; +import com.arialyy.aria.util.CommonUtil; import com.arialyy.aria.util.Regular; import java.io.BufferedReader; +import java.io.File; +import java.io.FileOutputStream; import java.io.IOException; +import java.io.InputStream; import java.io.InputStreamReader; import java.net.HttpURLConnection; import java.net.URL; @@ -42,19 +47,28 @@ import java.util.regex.Matcher; import java.util.regex.Pattern; /** - * 解析url中获取到到m3u信息 + * 解析url中获取到到m3u8文件信息 * https://www.cnblogs.com/renhui/p/10351870.html * https://blog.csdn.net/Guofengpu/article/details/54922865 */ -public class M3U8FileInfoThread implements Runnable { +final class M3U8InfoThread implements Runnable { private final String TAG = "M3U8FileInfoThread"; private DownloadEntity mEntity; private DTaskWrapper mTaskWrapper; private int mConnectTimeOut; private OnFileInfoCallback onFileInfoCallback; + private OnGetLivePeerCallback onGetPeerCallback; private HttpTaskConfig mTaskDelegate; + /** + * 是否停止获取切片信息,{@code true}停止获取切片信息 + */ + private boolean isStop = false; + + interface OnGetLivePeerCallback { + void onGetPeer(String url); + } - public M3U8FileInfoThread(DTaskWrapper taskWrapper, OnFileInfoCallback callback) { + M3U8InfoThread(DTaskWrapper taskWrapper, OnFileInfoCallback callback) { this.mTaskWrapper = taskWrapper; mEntity = taskWrapper.getEntity(); mConnectTimeOut = @@ -93,12 +107,24 @@ public class M3U8FileInfoThread implements Runnable { return; } List extInf = new ArrayList<>(); + boolean isLive = mTaskWrapper.getRequestType() == ITaskWrapper.M3U8_LIVE; while ((line = reader.readLine()) != null) { + if (isStop){ + break; + } if (line.startsWith("#EXT-X-ENDLIST")) { break; } + ALog.d(TAG, line); if (line.startsWith("#EXTINF")) { - extInf.add(reader.readLine()); + String info = reader.readLine(); + if (isLive) { + if (onGetPeerCallback != null) { + onGetPeerCallback.onGetPeer(info); + } + } else { + extInf.add(info); + } } else if (line.startsWith("#EXT-X-STREAM-INF")) { int setBand = mTaskWrapper.asM3U8().getBandWidth(); int bandWidth = getBandWidth(line); @@ -110,10 +136,12 @@ public class M3U8FileInfoThread implements Runnable { failDownload(String.format("【%s】码率不存在", bandWidth), false); } return; + } else if (line.startsWith("EXT-X-KEY")) { + getKeyInfo(line); } } - if (extInf.isEmpty()) { + if (!isLive && extInf.isEmpty()) { failDownload(String.format("获取M3U8下载地址列表失败,url: %s", mEntity.getUrl()), false); return; } @@ -133,14 +161,48 @@ public class M3U8FileInfoThread implements Runnable { } } + /** + * 是否停止获取切片信息,{@code true}停止获取切片信息 + */ + public void setStop(boolean isStop) { + this.isStop = isStop; + } + + /** + * 直播切片信息获取回调 + */ + public void setOnGetPeerCallback(OnGetLivePeerCallback peerCallback) { + onGetPeerCallback = peerCallback; + } + + /** + * 获取加密的密钥信息 + */ + private void getKeyInfo(String line) { + String temp = line.substring(line.indexOf(":") + 1); + String[] params = temp.split(","); + M3U8KeyInfo keyInfo = new M3U8KeyInfo(); + for (String param : params) { + if (param.startsWith("METHOD")) { + keyInfo.method = param.split("=")[1]; + } else if (param.startsWith("URI")) { + keyInfo.keyUrl = param.split("=")[1].replaceAll("\"", ""); + keyInfo.keyPath = new File(mEntity.getFilePath()).getParent() + "/" + CommonUtil.getStrMd5( + keyInfo.keyUrl) + ".key"; + } else if (param.startsWith("IV")) { + keyInfo.iv = param.split("=")[1]; + } + } + mTaskWrapper.asM3U8().setKeyInfo(keyInfo); + DownloadKey(keyInfo); + } + /** * 读取bandwidth */ private int getBandWidth(String line) { Pattern p = Pattern.compile(Regular.BANDWIDTH); - Matcher m = p.matcher(line); - if (m.find()) { return Integer.parseInt(m.group()); } @@ -210,4 +272,46 @@ public class M3U8FileInfoThread implements Runnable { private void failDownload(String errorInfo, boolean needRetry) { onFileInfoCallback.onFail(mEntity, new M3U8Exception(TAG, errorInfo), needRetry); } + + /** + * 密钥不存在,下载密钥 + */ + private void DownloadKey(M3U8KeyInfo info) { + HttpURLConnection conn = null; + FileOutputStream fos = null; + try { + File keyF = new File(info.keyPath); + if (!keyF.exists()) { + ALog.d(TAG, "密钥不存在,下载密钥"); + CommonUtil.createFile(keyF.getPath()); + } else { + return; + } + URL url = ConnectionHelp.handleUrl(info.keyUrl, mTaskDelegate); + conn = ConnectionHelp.handleConnection(url, mTaskDelegate); + ConnectionHelp.setConnectParam(mTaskDelegate, conn); + conn.setConnectTimeout(mConnectTimeOut); + conn.connect(); + InputStream is = conn.getInputStream(); + fos = new FileOutputStream(keyF); + byte[] buffer = new byte[1024]; + int len; + while ((len = is.read(buffer)) != -1) { + fos.write(buffer, 0, len); + } + } catch (Exception e) { + e.printStackTrace(); + } finally { + try { + if (fos != null) { + fos.close(); + } + if (conn != null) { + conn.disconnect(); + } + } catch (IOException e) { + e.printStackTrace(); + } + } + } } diff --git a/Aria/src/main/java/com/arialyy/aria/core/download/m3u8/LiveProtocol.java b/Aria/src/main/java/com/arialyy/aria/core/download/m3u8/M3U8KeyInfo.java similarity index 70% rename from Aria/src/main/java/com/arialyy/aria/core/download/m3u8/LiveProtocol.java rename to Aria/src/main/java/com/arialyy/aria/core/download/m3u8/M3U8KeyInfo.java index fed871e4..b5e646f5 100644 --- a/Aria/src/main/java/com/arialyy/aria/core/download/m3u8/LiveProtocol.java +++ b/Aria/src/main/java/com/arialyy/aria/core/download/m3u8/M3U8KeyInfo.java @@ -15,17 +15,28 @@ */ package com.arialyy.aria.core.download.m3u8; -import android.support.annotation.IntDef; -import java.lang.annotation.Retention; -import java.lang.annotation.RetentionPolicy; - /** - * 直播协议 + * m3u8加密密钥信息 */ -@IntDef({ - LiveProtocol.HLS -}) -@Retention(RetentionPolicy.SOURCE) -public @interface LiveProtocol { - int HLS = 1; +public class M3U8KeyInfo { + + /** + * 加密key保存地址 + */ + public String keyPath; + + /** + * 加密key的下载地址 + */ + public String keyUrl; + + /** + * 加密算法 + */ + public String method; + + /** + * key的iv值 + */ + public String iv; } diff --git a/Aria/src/main/java/com/arialyy/aria/core/download/m3u8/M3U8LiveDelegate.java b/Aria/src/main/java/com/arialyy/aria/core/download/m3u8/M3U8LiveDelegate.java index c5615006..699b6329 100644 --- a/Aria/src/main/java/com/arialyy/aria/core/download/m3u8/M3U8LiveDelegate.java +++ b/Aria/src/main/java/com/arialyy/aria/core/download/m3u8/M3U8LiveDelegate.java @@ -16,6 +16,7 @@ package com.arialyy.aria.core.download.m3u8; import com.arialyy.aria.core.common.BaseDelegate; +import com.arialyy.aria.core.download.DTaskWrapper; import com.arialyy.aria.core.inf.AbsTarget; import com.arialyy.aria.core.inf.AbsTaskWrapper; @@ -29,20 +30,14 @@ public class M3U8LiveDelegate extends BaseDelegate mFlagQueue = new ArrayBlockingQueue<>(EXEC_MAX_NUM); + private LiveStateManager mManager; + private ReentrantLock LOCK = new ReentrantLock(); + private Condition mCondition = LOCK.newCondition(); + private LinkedBlockingQueue mPeerQueue = new LinkedBlockingQueue<>(); + + M3U8LiveLoader(IEventListener listener, DTaskWrapper wrapper) { + super(listener, wrapper); + } + + @Override protected IThreadState getStateManager(Looper looper) { + mManager = new LiveStateManager(looper, mListener); + mStateHandler = new Handler(looper, mManager); + return mManager; + } + + void offerPeer(String peerUrl) { + mPeerQueue.offer(peerUrl); + } + + @Override protected void handleTask() { + + new Thread(new Runnable() { + @Override public void run() { + String cacheDir = getCacheDir(); + int index = 0; + while (!isBreak()) { + try { + LOCK.lock(); + while (mFlagQueue.size() < EXEC_MAX_NUM) { + String url = mPeerQueue.poll(); + if (url == null) { + break; + } + M3U8ThreadTask task = createThreadTask(cacheDir, index, url); + getTaskList().put(index, task); + mFlagQueue.offer(startThreadTask(task)); + index++; + } + if (mFlagQueue.size() > 0) { + mCondition.await(); + } + } catch (InterruptedException e) { + e.printStackTrace(); + } finally { + LOCK.unlock(); + } + } + } + }).start(); + } + + @Override protected void setMaxSpeed(int maxSpeed) { + // TODO: 2019-06-06 展不支持 + } + + private void notifyLock() { + try { + LOCK.lock(); + long id = mFlagQueue.take(); + ALog.d(TAG, String.format("线程【%s】完成", id)); + mCondition.signalAll(); + } catch (InterruptedException e) { + e.printStackTrace(); + } finally { + LOCK.unlock(); + } + } + + /** + * 启动线程任务 + * + * @return 线程唯一id标志 + */ + private long startThreadTask(M3U8ThreadTask task) { + ThreadTaskManager.getInstance().startThread(mTaskWrapper.getKey(), task); + return IdGenerator.getInstance().nextId(); + } + + /** + * 配置config + */ + private M3U8ThreadTask createThreadTask(String cacheDir, int indexId, String tsUrl) { + ThreadRecord record = new ThreadRecord(); + record.key = mRecord.filePath; + record.isComplete = false; + record.tsUrl = tsUrl; + record.threadType = TaskRecord.TYPE_M3U8_LIVE; + record.threadId = indexId; + + SubThreadConfig config = new SubThreadConfig<>(); + config.url = tsUrl; + config.tempFile = new File(getTsFilePath(cacheDir, indexId)); + config.isBlock = mRecord.isBlock; + config.isOpenDynamicFile = mRecord.isOpenDynamicFile; + config.taskWrapper = mTaskWrapper; + config.record = record; + config.stateHandler = mStateHandler; + + if (!config.tempFile.exists()) { + CommonUtil.createFile(config.tempFile.getPath()); + } + return new M3U8ThreadTask(config); + } + + /** + * 合并文件 + * + * @return {@code true} 合并成功,{@code false}合并失败 + */ + public boolean mergeFile() { + ITsMergeHandler mergeHandler = mTaskWrapper.asM3U8().getMergeHandler(); + String cacheDir = getCacheDir(); + List partPath = new ArrayList<>(); + String[] tsNames = new File(cacheDir).list(new FilenameFilter() { + @Override public boolean accept(File dir, String name) { + return name.endsWith(".ts"); + } + }); + for (String tsName : tsNames) { + partPath.add(cacheDir + "/" + tsName); + } + + boolean isSuccess; + if (mergeHandler != null) { + isSuccess = mergeHandler.merge(mTaskWrapper.asM3U8().getKeyInfo(), partPath); + if (mergeHandler.getClass().isAnonymousClass()) { + mTaskWrapper.asM3U8().setMergeHandler(null); + } + } else { + isSuccess = FileUtil.mergeFile(mEntity.getFilePath(), partPath); + } + if (isSuccess) { + // 合并成功,删除缓存文件 + for (String pp : partPath) { + File f = new File(pp); + if (f.exists()) { + f.delete(); + } + } + File cDir = new File(cacheDir); + if (cDir.exists()) { + cDir.delete(); + } + return true; + } else { + ALog.e(TAG, "合并失败"); + return false; + } + } + + /** + * M3U8线程状态管理 + */ + private class LiveStateManager implements IThreadState { + private final String TAG = "M3U8ThreadStateManager"; + + /** + * 任务状态回调 + */ + private IEventListener mListener; + private int mCancelNum = 0; // 已经取消的线程的数 + private int mStopNum = 0; // 已经停止的线程数 + private long mProgress; //当前总进度 + private Looper mLooper; + + /** + * @param listener 任务事件 + */ + LiveStateManager(Looper looper, IEventListener listener) { + mLooper = looper; + mListener = listener; + } + + /** + * 退出looper循环 + */ + private void quitLooper() { + ALog.d(TAG, "quitLooper"); + mLooper.quit(); + } + + @Override public boolean handleMessage(Message msg) { + switch (msg.what) { + case STATE_STOP: + mStopNum++; + if (isStop()) { + ALog.d(TAG, "任务停止"); + mListener.onStop(mProgress); + quitLooper(); + } + break; + case STATE_CANCEL: + mCancelNum++; + if (isCancel()) { + ALog.d(TAG, "任务取消"); + mListener.onCancel(); + quitLooper(); + } + break; + case STATE_COMPLETE: + notifyLock(); + break; + case STATE_RUNNING: + mProgress += (long) msg.obj; + break; + } + return false; + } + + @Override public boolean isStop() { + // 某些服务器后一次性发送多个ts地址,所以不能简单使用mStopNum == mFlagQueue.size();判断状态 + //return mStopNum == mFlagQueue.size(); + return false; + } + + @Override public boolean isFail() { + // 直播下载不处理失败的切片 + return false; + } + + @Override public boolean isComplete() { + // 直播不处理完成 + return false; + } + + @Override public boolean isCancel() { + //return mCancelNum == mFlagQueue.size(); + return false; + } + + @Override public long getCurrentProgress() { + return mProgress; + } + } +} diff --git a/Aria/src/main/java/com/arialyy/aria/core/download/m3u8/M3U8TaskConfig.java b/Aria/src/main/java/com/arialyy/aria/core/download/m3u8/M3U8TaskConfig.java index 61f93862..9f4e2703 100644 --- a/Aria/src/main/java/com/arialyy/aria/core/download/m3u8/M3U8TaskConfig.java +++ b/Aria/src/main/java/com/arialyy/aria/core/download/m3u8/M3U8TaskConfig.java @@ -30,7 +30,7 @@ public class M3U8TaskConfig { /** * #EXTINF 标签信息处理器 */ - private ITsUrlConverter tsUrlConverter; + private IVodTsUrlConverter vodUrlConverter; /** * 缓存目录 @@ -72,6 +72,33 @@ public class M3U8TaskConfig { */ private String bandWidthUrl; + /** + * ts密钥信息 + */ + private M3U8KeyInfo keyInfo; + + /** + * 直播下载,ts url转换器 + */ + private ILiveTsUrlConverter liveTsUrlConverter; + + public ILiveTsUrlConverter getLiveTsUrlConverter() { + return liveTsUrlConverter; + } + + public void setLiveTsUrlConverter( + ILiveTsUrlConverter liveTsUrlConverter) { + this.liveTsUrlConverter = liveTsUrlConverter; + } + + public M3U8KeyInfo getKeyInfo() { + return keyInfo; + } + + public void setKeyInfo(M3U8KeyInfo keyInfo) { + this.keyInfo = keyInfo; + } + public String getBandWidthUrl() { return bandWidthUrl; } @@ -129,12 +156,12 @@ public class M3U8TaskConfig { this.mergeHandler = mergeHandler; } - public ITsUrlConverter getTsUrlConverter() { - return tsUrlConverter; + public IVodTsUrlConverter getVodUrlConverter() { + return vodUrlConverter; } - public void setTsUrlConverter(ITsUrlConverter tsUrlConverter) { - this.tsUrlConverter = tsUrlConverter; + public void setVodUrlConverter(IVodTsUrlConverter vodUrlConverter) { + this.vodUrlConverter = vodUrlConverter; } public List getUrls() { diff --git a/Aria/src/main/java/com/arialyy/aria/core/download/m3u8/M3U8FileUtil.java b/Aria/src/main/java/com/arialyy/aria/core/download/m3u8/M3U8VodDownloadUtil.java similarity index 58% rename from Aria/src/main/java/com/arialyy/aria/core/download/m3u8/M3U8FileUtil.java rename to Aria/src/main/java/com/arialyy/aria/core/download/m3u8/M3U8VodDownloadUtil.java index 4ae13974..5b87c1d4 100644 --- a/Aria/src/main/java/com/arialyy/aria/core/download/m3u8/M3U8FileUtil.java +++ b/Aria/src/main/java/com/arialyy/aria/core/download/m3u8/M3U8VodDownloadUtil.java @@ -22,10 +22,8 @@ import com.arialyy.aria.core.common.OnFileInfoCallback; import com.arialyy.aria.core.download.DTaskWrapper; import com.arialyy.aria.core.inf.AbsEntity; import com.arialyy.aria.core.inf.IDownloadListener; -import com.arialyy.aria.core.inf.ITaskWrapper; import com.arialyy.aria.exception.BaseException; import com.arialyy.aria.exception.M3U8Exception; -import com.arialyy.aria.util.ALog; import java.util.ArrayList; import java.util.Collection; import java.util.List; @@ -38,19 +36,19 @@ import java.util.List; * 3、完成所有分片下载后,合并ts文件 * 4、删除该隐藏文件夹 */ -public class M3U8FileUtil implements IUtil { +public class M3U8VodDownloadUtil implements IUtil { private final String TAG = "M3U8DownloadUtil"; private DTaskWrapper mWrapper; private IDownloadListener mListener; private boolean isStop = false, isCancel = false; private List mUrls = new ArrayList<>(); - private M3U8FileLoader mLoader; + private M3U8VodLoader mLoader; - public M3U8FileUtil(DTaskWrapper wrapper, IDownloadListener listener) { + public M3U8VodDownloadUtil(DTaskWrapper wrapper, IDownloadListener listener) { mWrapper = wrapper; mListener = listener; - mLoader = new M3U8FileLoader(mListener, mWrapper); + mLoader = new M3U8VodLoader(mListener, mWrapper); } @Override public String getKey() { @@ -84,57 +82,49 @@ public class M3U8FileUtil implements IUtil { return; } mListener.onPre(); - - new Thread(createInfoThread()).start(); - } - - @Override public void resume() { - start(); + getVodInfo(); } @Override public void setMaxSpeed(int speed) { mLoader.setMaxSpeed(speed); } - private Runnable createInfoThread() { - if (mWrapper.getRequestType() == ITaskWrapper.M3U8_FILE) { - return new M3U8FileInfoThread(mWrapper, new OnFileInfoCallback() { - @Override public void onComplete(String key, CompleteInfo info) { - ITsUrlConverter handler = mWrapper.asM3U8().getTsUrlConverter(); - if (handler != null) { - if (TextUtils.isEmpty(mWrapper.asM3U8().getBandWidthUrl())) { - mUrls.addAll(handler.convert(mWrapper.getEntity().getUrl(), (List) info.obj)); - } else { - mUrls.addAll( - handler.convert(mWrapper.asM3U8().getBandWidthUrl(), (List) info.obj)); - } - if (handler.getClass().isAnonymousClass()) { - mWrapper.asM3U8().setTsUrlConverter(null); - } + /** + * 获取点播文件信息 + */ + private void getVodInfo() { + M3U8InfoThread thread = new M3U8InfoThread(mWrapper, new OnFileInfoCallback() { + @Override public void onComplete(String key, CompleteInfo info) { + IVodTsUrlConverter converter = mWrapper.asM3U8().getVodUrlConverter(); + if (converter != null) { + if (TextUtils.isEmpty(mWrapper.asM3U8().getBandWidthUrl())) { + mUrls.addAll(converter.convert(mWrapper.getEntity().getUrl(), (List) info.obj)); } else { - mUrls.addAll((Collection) info.obj); + mUrls.addAll( + converter.convert(mWrapper.asM3U8().getBandWidthUrl(), (List) info.obj)); } - if (mUrls.isEmpty()) { - failDownload(new M3U8Exception(TAG, "获取地址失败"), false); - return; - } else if (!mUrls.get(0).startsWith("http")) { - failDownload(new M3U8Exception(TAG, "地址错误,请使用IM3U8UrlExtInfHandler处理你的url信息"), false); - return; + if (converter.getClass().isAnonymousClass()) { + mWrapper.asM3U8().setVodUrlConverter(null); } - mWrapper.asM3U8().setUrls(mUrls); - mLoader.start(); + } else { + mUrls.addAll((Collection) info.obj); } - - @Override public void onFail(AbsEntity entity, BaseException e, boolean needRetry) { - failDownload(e, needRetry); + if (mUrls.isEmpty()) { + failDownload(new M3U8Exception(TAG, "获取地址失败"), false); + return; + } else if (!mUrls.get(0).startsWith("http")) { + failDownload(new M3U8Exception(TAG, "地址错误,请使用IM3U8UrlExtInfHandler处理你的url信息"), false); + return; } - }); - } else if (mWrapper.getRequestType() == ITaskWrapper.M3U8_LIVE) { - return null; - } else { - ALog.e(TAG, "不支持的类型"); - return null; - } + mWrapper.asM3U8().setUrls(mUrls); + mLoader.start(); + } + + @Override public void onFail(AbsEntity entity, BaseException e, boolean needRetry) { + failDownload(e, needRetry); + } + }); + new Thread(thread).start(); } private void failDownload(BaseException e, boolean needRetry) { diff --git a/Aria/src/main/java/com/arialyy/aria/core/download/m3u8/M3U8FileLoader.java b/Aria/src/main/java/com/arialyy/aria/core/download/m3u8/M3U8VodLoader.java similarity index 89% rename from Aria/src/main/java/com/arialyy/aria/core/download/m3u8/M3U8FileLoader.java rename to Aria/src/main/java/com/arialyy/aria/core/download/m3u8/M3U8VodLoader.java index 659e62b8..65ee3551 100644 --- a/Aria/src/main/java/com/arialyy/aria/core/download/m3u8/M3U8FileLoader.java +++ b/Aria/src/main/java/com/arialyy/aria/core/download/m3u8/M3U8VodLoader.java @@ -19,13 +19,11 @@ import android.os.Bundle; import android.os.Handler; import android.os.Looper; import android.os.Message; -import com.arialyy.aria.core.common.AbsFileer; import com.arialyy.aria.core.common.IThreadState; import com.arialyy.aria.core.common.SubThreadConfig; import com.arialyy.aria.core.common.TaskRecord; import com.arialyy.aria.core.common.ThreadRecord; import com.arialyy.aria.core.download.DTaskWrapper; -import com.arialyy.aria.core.download.DownloadEntity; import com.arialyy.aria.core.inf.IEventListener; import com.arialyy.aria.core.manager.ThreadTaskManager; import com.arialyy.aria.exception.BaseException; @@ -43,49 +41,27 @@ import java.util.concurrent.locks.ReentrantLock; /** * M3U8点播文件下载器 */ -public class M3U8FileLoader extends AbsFileer { +public class M3U8VodLoader extends BaseM3U8Loader { /** * 最大执行数 */ private static final int EXEC_MAX_NUM = 4; private Handler mStateHandler; private ArrayBlockingQueue mFlagQueue = new ArrayBlockingQueue<>(EXEC_MAX_NUM); - private M3U8ThreadStateManager mManager; + private VodStateManager mManager; private ReentrantLock LOCK = new ReentrantLock(); private Condition mCondition = LOCK.newCondition(); - M3U8FileLoader(IEventListener listener, DTaskWrapper wrapper) { + M3U8VodLoader(IEventListener listener, DTaskWrapper wrapper) { super(listener, wrapper); } @Override protected IThreadState getStateManager(Looper looper) { - mManager = new M3U8ThreadStateManager(looper, mRecord, mListener); + mManager = new VodStateManager(looper, mRecord, mListener); mStateHandler = new Handler(looper, mManager); return mManager; } - @Override protected long delayTimer() { - return 2000; - } - - /** - * 获取ts文件保存路径 - * - * @param dirCache 缓存目录 - * @param threadId ts文件名 - */ - public static String getTsFilePath(String dirCache, int threadId) { - return String.format("%s/%s.ts", dirCache, threadId); - } - - private String getCacheDir() { - String cacheDir = mTaskWrapper.asM3U8().getCacheDir(); - if (!new File(cacheDir).exists()) { - CommonUtil.createDir(cacheDir); - } - return cacheDir; - } - @Override protected void handleTask() { new Thread(new Runnable() { @@ -165,7 +141,7 @@ public class M3U8FileLoader extends AbsFileer { */ private M3U8ThreadTask createThreadTask(String cacheDir, ThreadRecord record) { SubThreadConfig config = new SubThreadConfig<>(); - config.url = record.m3u8url; + config.url = record.tsUrl; config.tempFile = new File(getTsFilePath(cacheDir, record.threadId)); config.isBlock = mRecord.isBlock; config.isOpenDynamicFile = mRecord.isOpenDynamicFile; @@ -181,7 +157,7 @@ public class M3U8FileLoader extends AbsFileer { /** * M3U8线程状态管理 */ - private class M3U8ThreadStateManager implements IThreadState { + private class VodStateManager implements IThreadState { private final String TAG = "M3U8ThreadStateManager"; /** @@ -201,7 +177,7 @@ public class M3U8FileLoader extends AbsFileer { * @param taskRecord 任务记录 * @param listener 任务事件 */ - M3U8ThreadStateManager(Looper looper, TaskRecord taskRecord, IEventListener listener) { + VodStateManager(Looper looper, TaskRecord taskRecord, IEventListener listener) { mLooper = looper; mTaskRecord = taskRecord; for (ThreadRecord record : taskRecord.threadRecords) { @@ -330,7 +306,7 @@ public class M3U8FileLoader extends AbsFileer { } boolean isSuccess; if (mergeHandler != null) { - isSuccess = mergeHandler.merge(partPath); + isSuccess = mergeHandler.merge(mTaskWrapper.asM3U8().getKeyInfo(), partPath); if (mergeHandler.getClass().isAnonymousClass()) { mTaskWrapper.asM3U8().setMergeHandler(null); } diff --git a/Aria/src/main/java/com/arialyy/aria/core/inf/ITaskWrapper.java b/Aria/src/main/java/com/arialyy/aria/core/inf/ITaskWrapper.java index feeaced2..a99b8707 100644 --- a/Aria/src/main/java/com/arialyy/aria/core/inf/ITaskWrapper.java +++ b/Aria/src/main/java/com/arialyy/aria/core/inf/ITaskWrapper.java @@ -50,9 +50,9 @@ public interface ITaskWrapper { int U_FTP = 6; /** - * M3u8文件 + * M3u8点播 */ - int M3U8_FILE = 7; + int M3U8_VOD = 7; /** * m3u8直播 diff --git a/Aria/src/main/java/com/arialyy/aria/core/upload/uploader/SimpleUploadUtil.java b/Aria/src/main/java/com/arialyy/aria/core/upload/uploader/SimpleUploadUtil.java index b6263dfe..984a042a 100644 --- a/Aria/src/main/java/com/arialyy/aria/core/upload/uploader/SimpleUploadUtil.java +++ b/Aria/src/main/java/com/arialyy/aria/core/upload/uploader/SimpleUploadUtil.java @@ -109,10 +109,6 @@ public class SimpleUploadUtil implements IUtil, Runnable { new Thread(this).start(); } - @Override public void resume() { - mUploader.cancel(); - } - @Override public void setMaxSpeed(int speed) { mUploader.setMaxSpeed(speed); } diff --git a/Aria/src/main/java/com/arialyy/aria/orm/DBConfig.java b/Aria/src/main/java/com/arialyy/aria/orm/DBConfig.java index 4745e668..aee68a8c 100644 --- a/Aria/src/main/java/com/arialyy/aria/orm/DBConfig.java +++ b/Aria/src/main/java/com/arialyy/aria/orm/DBConfig.java @@ -36,7 +36,7 @@ class DBConfig { static boolean DEBUG = false; static Map mapping = new HashMap<>(); static String DB_NAME; - static int VERSION = 47; + static int VERSION = 48; /** * 是否将数据库保存在Sd卡,{@code true} 是 diff --git a/Aria/src/main/java/com/arialyy/aria/util/RecordUtil.java b/Aria/src/main/java/com/arialyy/aria/util/RecordUtil.java index c1caa8f2..3f77a160 100644 --- a/Aria/src/main/java/com/arialyy/aria/util/RecordUtil.java +++ b/Aria/src/main/java/com/arialyy/aria/util/RecordUtil.java @@ -22,7 +22,7 @@ import com.arialyy.aria.core.common.TaskRecord; import com.arialyy.aria.core.common.ThreadRecord; import com.arialyy.aria.core.download.DownloadEntity; import com.arialyy.aria.core.download.DownloadGroupEntity; -import com.arialyy.aria.core.download.m3u8.M3U8FileLoader; +import com.arialyy.aria.core.download.m3u8.BaseM3U8Loader; import com.arialyy.aria.core.inf.AbsEntity; import com.arialyy.aria.core.inf.AbsNormalEntity; import com.arialyy.aria.core.upload.UploadEntity; @@ -149,7 +149,7 @@ public class RecordUtil { * 处理任务未完成的情况 */ if (!entity.isComplete()) { - if (record.taskType == TaskRecord.TYPE_M3U8) { // 删除ts分片文件 + if (record.taskType == TaskRecord.TYPE_M3U8_VOD) { // 删除ts分片文件 String cacheDir = null; if (!targetFile.isDirectory()) { cacheDir = targetFile.getParent() + "/." + targetFile.getName(); @@ -207,7 +207,7 @@ public class RecordUtil { * 处理任务未完成的情况 */ if (!entity.isComplete()) { - if (record.taskType == TaskRecord.TYPE_M3U8) { // 删除ts分片文件 + if (record.taskType == TaskRecord.TYPE_M3U8_VOD) { // 删除ts分片文件 String cacheDir = null; if (!targetFile.isDirectory()) { cacheDir = targetFile.getParent() + "/." + targetFile.getName(); @@ -289,7 +289,7 @@ public class RecordUtil { if (!TextUtils.isEmpty(cacheDir)) { List partPath = new ArrayList<>(); for (ThreadRecord tr : record.threadRecords) { - partPath.add(M3U8FileLoader.getTsFilePath(cacheDir, tr.threadId)); + partPath.add(BaseM3U8Loader.getTsFilePath(cacheDir, tr.threadId)); } for (String pp : partPath) { File f = new File(pp); diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index e4beab9d..646511c1 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -47,7 +47,8 @@ android:screenOrientation="landscape" android:theme="@style/FullScreen" android:windowSoftInputMode="stateHidden|adjustResize"/> - + + diff --git a/app/src/main/java/com/arialyy/simple/MainActivity.java b/app/src/main/java/com/arialyy/simple/MainActivity.java index f9f6c36f..0fa9d955 100644 --- a/app/src/main/java/com/arialyy/simple/MainActivity.java +++ b/app/src/main/java/com/arialyy/simple/MainActivity.java @@ -36,7 +36,8 @@ import com.arialyy.simple.base.BaseActivity; import com.arialyy.simple.base.adapter.AbsHolder; import com.arialyy.simple.base.adapter.AbsRVAdapter; import com.arialyy.simple.base.adapter.RvItemClickSupport; -import com.arialyy.simple.core.download.m3u8.M3U8DownloadActivity; +import com.arialyy.simple.core.download.m3u8.M3U8LiveDownloadActivity; +import com.arialyy.simple.core.download.m3u8.M3U8VodDownloadActivity; import com.arialyy.simple.databinding.ActivityMainBinding; import com.arialyy.simple.core.download.DownloadActivity; import com.arialyy.simple.core.download.FtpDownloadActivity; @@ -109,7 +110,11 @@ public class MainActivity extends BaseActivity { break; case 6: module.startNextActivity(MainActivity.this, data.get(position), - M3U8DownloadActivity.class); + M3U8VodDownloadActivity.class); + break; + case 7: + module.startNextActivity(MainActivity.this, data.get(position), + M3U8LiveDownloadActivity.class); break; } } diff --git a/app/src/main/java/com/arialyy/simple/core/download/m3u8/M3U8LiveDownloadActivity.java b/app/src/main/java/com/arialyy/simple/core/download/m3u8/M3U8LiveDownloadActivity.java new file mode 100644 index 00000000..fb3119b4 --- /dev/null +++ b/app/src/main/java/com/arialyy/simple/core/download/m3u8/M3U8LiveDownloadActivity.java @@ -0,0 +1,270 @@ +/* + * Copyright (C) 2016 AriaLyy(https://github.com/AriaLyy/Aria) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.arialyy.simple.core.download.m3u8; + +import android.arch.lifecycle.Observer; +import android.arch.lifecycle.ViewModelProviders; +import android.os.Bundle; +import android.support.annotation.Nullable; +import android.util.Log; +import android.view.Menu; +import android.view.MenuItem; +import android.view.View; +import android.widget.Toast; +import com.arialyy.annotations.Download; +import com.arialyy.aria.core.Aria; +import com.arialyy.aria.core.download.DownloadEntity; +import com.arialyy.aria.core.download.DownloadTarget; +import com.arialyy.aria.core.download.DownloadTask; +import com.arialyy.aria.core.download.m3u8.ILiveTsUrlConverter; +import com.arialyy.aria.util.ALog; +import com.arialyy.aria.util.CommonUtil; +import com.arialyy.frame.util.show.T; +import com.arialyy.simple.R; +import com.arialyy.simple.base.BaseActivity; +import com.arialyy.simple.common.ModifyPathDialog; +import com.arialyy.simple.common.ModifyUrlDialog; +import com.arialyy.simple.databinding.ActivityM3u8LiveBinding; +import java.io.File; + +public class M3U8LiveDownloadActivity extends BaseActivity { + + private String mUrl; + private String mFilePath; + private M3U8LiveModule mModule; + private DownloadTarget mTarget; + + @Override + protected void init(Bundle savedInstanceState) { + super.init(savedInstanceState); + setTitle(getString(R.string.m3u8_live)); + Aria.download(this).register(); + mModule = ViewModelProviders.of(this).get(M3U8LiveModule.class); + mModule.getHttpDownloadInfo(this).observe(this, new Observer() { + + @Override public void onChanged(@Nullable DownloadEntity entity) { + if (entity == null) { + return; + } + mTarget = Aria.download(M3U8LiveDownloadActivity.this).load(entity.getUrl()); + getBinding().setStateStr(getString(R.string.start)); + getBinding().setUrl(entity.getUrl()); + getBinding().setFilePath(entity.getFilePath()); + mUrl = entity.getUrl(); + mFilePath = entity.getFilePath(); + } + }); + getBinding().setViewModel(this); + } + + public void chooseUrl() { + ModifyUrlDialog dialog = + new ModifyUrlDialog(this, getString(R.string.modify_url_dialog_title), mUrl); + dialog.show(getSupportFragmentManager(), "ModifyUrlDialog"); + } + + public void chooseFilePath() { + ModifyPathDialog dialog = + new ModifyPathDialog(this, getString(R.string.modify_file_path), mFilePath); + dialog.show(getSupportFragmentManager(), "ModifyPathDialog"); + } + + @Override + public boolean onCreateOptionsMenu(Menu menu) { + getMenuInflater().inflate(R.menu.menu_single_task_activity, menu); + return super.onCreateOptionsMenu(menu); + } + + @Override + public boolean onMenuItemClick(MenuItem item) { + int speed = -1; + String msg = ""; + switch (item.getItemId()) { + case R.id.help: + msg = "一些小知识点:\n" + + "1、你可以在注解中增加链接,用于指定被注解的方法只能被特定的下载任务回调,以防止progress乱跳\n" + + "2、当遇到网络慢的情况时,你可以先使用onPre()更新UI界面,待连接成功时,再在onTaskPre()获取完整的task数据,然后给UI界面设置正确的数据\n" + + "3、你可以在界面初始化时通过Aria.download(this).load(URL).getPercent()等方法快速获取相关任务的一些数据"; + showMsgDialog("tip", msg); + break; + case R.id.speed_0: + speed = 0; + break; + case R.id.speed_128: + speed = 128; + break; + case R.id.speed_256: + speed = 256; + break; + case R.id.speed_512: + speed = 512; + break; + case R.id.speed_1m: + speed = 1024; + break; + } + if (speed > -1) { + msg = item.getTitle().toString(); + Aria.download(this).setMaxSpeed(speed); + T.showShort(this, msg); + } + return true; + } + + @Download.onWait + void onWait(DownloadTask task) { + if (task.getKey().equals(mUrl)) { + Log.d(TAG, "wait ==> " + task.getDownloadEntity().getFileName()); + } + } + + @Download.onPre + protected void onPre(DownloadTask task) { + if (task.getKey().equals(mUrl)) { + getBinding().setStateStr(getString(R.string.stop)); + } + } + + @Download.onTaskStart + void taskStart(DownloadTask task) { + if (task.getKey().equals(mUrl)) { + getBinding().setFileSize(task.getConvertFileSize()); + ALog.d(TAG, "isComplete = " + task.isComplete() + ", state = " + task.getState()); + } + } + + @Download.onTaskRunning + protected void running(DownloadTask task) { + if (task.getKey().equals(mUrl)) { + ALog.d(TAG, "isRunning"); + getBinding().setProgress(task.getPercent()); + getBinding().setSpeed(task.getConvertSpeed()); + } + } + + @Download.onTaskResume + void taskResume(DownloadTask task) { + if (task.getKey().equals(mUrl)) { + getBinding().setStateStr(getString(R.string.stop)); + } + } + + @Download.onTaskStop + void taskStop(DownloadTask task) { + if (task.getKey().equals(mUrl)) { + getBinding().setStateStr(getString(R.string.resume)); + getBinding().setSpeed(""); + } + } + + @Download.onTaskCancel + void taskCancel(DownloadTask task) { + if (task.getKey().equals(mUrl)) { + getBinding().setProgress(0); + getBinding().setStateStr(getString(R.string.start)); + getBinding().setSpeed(""); + Log.d(TAG, "cancel"); + } + } + + @Download.onTaskFail + void taskFail(DownloadTask task, Exception e) { + if (task.getKey().equals(mUrl)) { + Toast.makeText(M3U8LiveDownloadActivity.this, getString(R.string.download_fail), + Toast.LENGTH_SHORT) + .show(); + getBinding().setStateStr(getString(R.string.start)); + } + } + + @Download.onTaskComplete + void taskComplete(DownloadTask task) { + if (task.getKey().equals(mUrl)) { + getBinding().setProgress(100); + Toast.makeText(M3U8LiveDownloadActivity.this, getString(R.string.download_success), + Toast.LENGTH_SHORT).show(); + getBinding().setStateStr(getString(R.string.re_start)); + getBinding().setSpeed(""); + ALog.d(TAG, "md5: " + CommonUtil.getFileMD5(new File(task.getDownloadPath()))); + } + } + + @Override + protected int setLayoutId() { + return R.layout.activity_m3u8_live; + } + + public void onClick(View view) { + switch (view.getId()) { + case R.id.start: + ALog.d(TAG, "isRunning = " + mTarget.isRunning()); + if (mTarget.isRunning()) { + Aria.download(this).load(mUrl).stop(); + } else { + startD(); + } + break; + case R.id.cancel: + Aria.download(this).load(mUrl).cancel(true); + break; + } + } + + private void startD() { + Aria.download(M3U8LiveDownloadActivity.this) + .load(mUrl) + .useServerFileName(true) + .setFilePath(mFilePath, true) + .asM3U8() + //.setBandWidthUrlConverter(new IBandWidthUrlConverter() { + // @Override public String convert(String bandWidthUrl) { + // int index = mUrl.lastIndexOf("/"); + // return mUrl.substring(0, index + 1) + bandWidthUrl; + // } + //}) + .asLive() + .setLiveTsUrlConvert(new ILiveTsUrlConverter() { + @Override public String convert(String m3u8Url, String tsUrl) { + int index = m3u8Url.lastIndexOf("/"); + String parentUrl = m3u8Url.substring(0, index + 1); + return parentUrl + tsUrl; + } + }) + //.setLiveTsUrlConvert(new IVodTsUrlConverter() { + // @Override public List convert(String m3u8Url, List tsUrls) { + // int index = m3u8Url.lastIndexOf("/"); + // String parentUrl = m3u8Url.substring(0, index + 1); + // List newUrls = new ArrayList<>(); + // for (String url : tsUrls) { + // newUrls.add(parentUrl + url); + // } + // + // return newUrls; + // } + //}) + .start(); + } + + @Override protected void dataCallback(int result, Object data) { + super.dataCallback(result, data); + if (result == ModifyUrlDialog.MODIFY_URL_DIALOG_RESULT) { + mModule.uploadUrl(this, String.valueOf(data)); + } else if (result == ModifyPathDialog.MODIFY_PATH_RESULT) { + mModule.updateFilePath(this, String.valueOf(data)); + } + } +} \ No newline at end of file diff --git a/app/src/main/java/com/arialyy/simple/core/download/m3u8/M3U8LiveModule.java b/app/src/main/java/com/arialyy/simple/core/download/m3u8/M3U8LiveModule.java new file mode 100644 index 00000000..69a28bf5 --- /dev/null +++ b/app/src/main/java/com/arialyy/simple/core/download/m3u8/M3U8LiveModule.java @@ -0,0 +1,95 @@ +/* + * Copyright (C) 2016 AriaLyy(https://github.com/AriaLyy/Aria) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.arialyy.simple.core.download.m3u8; + +import android.arch.lifecycle.LiveData; +import android.arch.lifecycle.MutableLiveData; +import android.content.Context; +import android.os.Environment; +import android.text.TextUtils; +import com.arialyy.aria.core.Aria; +import com.arialyy.aria.core.download.DownloadEntity; +import com.arialyy.aria.util.ALog; +import com.arialyy.frame.base.BaseViewModule; +import com.arialyy.simple.util.AppUtil; +import java.io.File; + +public class M3U8LiveModule extends BaseViewModule { + private final String M3U8_LIVE_URL_KEY = "M3U8_LIVE_URL_KEY"; + private final String M3U8_LIVE_PATH_KEY = "M3U8_LIVE_PATH_KEY"; + // 多码率地址: + private final String defUrl = "http://ivi.bupt.edu.cn/hls/cctv6hd.m3u8"; + private final String filePath = + Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOWNLOADS).getPath() + + "/live.ts"; + + private MutableLiveData liveData = new MutableLiveData<>(); + private DownloadEntity singDownloadInfo; + + /** + * 单任务下载的信息 + */ + LiveData getHttpDownloadInfo(Context context) { + String url = AppUtil.getConfigValue(context, M3U8_LIVE_URL_KEY, defUrl); + String filePath = AppUtil.getConfigValue(context, M3U8_LIVE_PATH_KEY, this.filePath); + + singDownloadInfo = Aria.download(context).getDownloadEntity(url); + if (singDownloadInfo == null) { + singDownloadInfo = new DownloadEntity(); + singDownloadInfo.setUrl(url); + File temp = new File(this.filePath); + singDownloadInfo.setFilePath(filePath); + singDownloadInfo.setFileName(temp.getName()); + } else { + AppUtil.setConfigValue(context, M3U8_LIVE_PATH_KEY, singDownloadInfo.getDownloadPath()); + AppUtil.setConfigValue(context, M3U8_LIVE_URL_KEY, singDownloadInfo.getUrl()); + } + liveData.postValue(singDownloadInfo); + + return liveData; + } + + /** + * 更新文件保存路径 + * + * @param filePath 文件保存路径 + */ + void updateFilePath(Context context, String filePath) { + if (TextUtils.isEmpty(filePath)) { + ALog.e(TAG, "文件保存路径为空"); + return; + } + File temp = new File(filePath); + AppUtil.setConfigValue(context, M3U8_LIVE_PATH_KEY, filePath); + singDownloadInfo.setFileName(temp.getName()); + singDownloadInfo.setFilePath(filePath); + liveData.postValue(singDownloadInfo); + } + + /** + * 更新url + */ + void uploadUrl(Context context, String url) { + if (TextUtils.isEmpty(url)) { + ALog.e(TAG, "下载地址为空"); + return; + } + AppUtil.setConfigValue(context, M3U8_LIVE_URL_KEY, url); + singDownloadInfo.setUrl(url); + liveData.postValue(singDownloadInfo); + } +} diff --git a/app/src/main/java/com/arialyy/simple/core/download/m3u8/M3U8DownloadActivity.java b/app/src/main/java/com/arialyy/simple/core/download/m3u8/M3U8VodDownloadActivity.java similarity index 90% rename from app/src/main/java/com/arialyy/simple/core/download/m3u8/M3U8DownloadActivity.java rename to app/src/main/java/com/arialyy/simple/core/download/m3u8/M3U8VodDownloadActivity.java index 090f52e3..4731a50e 100644 --- a/app/src/main/java/com/arialyy/simple/core/download/m3u8/M3U8DownloadActivity.java +++ b/app/src/main/java/com/arialyy/simple/core/download/m3u8/M3U8VodDownloadActivity.java @@ -18,7 +18,6 @@ package com.arialyy.simple.core.download.m3u8; import android.arch.lifecycle.Observer; import android.arch.lifecycle.ViewModelProviders; -import android.net.Uri; import android.os.Bundle; import android.support.annotation.Nullable; import android.util.Log; @@ -32,9 +31,8 @@ import com.arialyy.aria.core.download.DownloadEntity; import com.arialyy.aria.core.download.DownloadTarget; import com.arialyy.aria.core.download.DownloadTask; import com.arialyy.aria.core.download.m3u8.IBandWidthUrlConverter; -import com.arialyy.aria.core.download.m3u8.ITsUrlConverter; +import com.arialyy.aria.core.download.m3u8.IVodTsUrlConverter; import com.arialyy.aria.core.inf.IEntity; -import com.arialyy.aria.core.inf.IHttpFileLenAdapter; import com.arialyy.aria.util.ALog; import com.arialyy.aria.util.CommonUtil; import com.arialyy.frame.util.show.T; @@ -42,17 +40,16 @@ import com.arialyy.simple.R; import com.arialyy.simple.base.BaseActivity; import com.arialyy.simple.common.ModifyPathDialog; import com.arialyy.simple.common.ModifyUrlDialog; -import com.arialyy.simple.databinding.ActivityM3u8Binding; +import com.arialyy.simple.databinding.ActivityM3u8VodBinding; import java.io.File; import java.util.ArrayList; import java.util.List; -import java.util.Map; -public class M3U8DownloadActivity extends BaseActivity { +public class M3U8VodDownloadActivity extends BaseActivity { private String mUrl; private String mFilePath; - private M3U8Module mModule; + private M3U8VodModule mModule; private DownloadTarget mTarget; @Override @@ -60,14 +57,14 @@ public class M3U8DownloadActivity extends BaseActivity { super.init(savedInstanceState); setTitle(getString(R.string.m3u8_file)); Aria.download(this).register(); - mModule = ViewModelProviders.of(this).get(M3U8Module.class); + mModule = ViewModelProviders.of(this).get(M3U8VodModule.class); mModule.getHttpDownloadInfo(this).observe(this, new Observer() { @Override public void onChanged(@Nullable DownloadEntity entity) { if (entity == null) { return; } - mTarget = Aria.download(M3U8DownloadActivity.this).load(entity.getUrl()); + mTarget = Aria.download(M3U8VodDownloadActivity.this).load(entity.getUrl()); if (mTarget.getTaskState() == IEntity.STATE_STOP) { getBinding().setStateStr(getString(R.string.resume)); } else if (mTarget.isRunning()) { @@ -200,7 +197,7 @@ public class M3U8DownloadActivity extends BaseActivity { @Download.onTaskFail void taskFail(DownloadTask task, Exception e) { if (task.getKey().equals(mUrl)) { - Toast.makeText(M3U8DownloadActivity.this, getString(R.string.download_fail), + Toast.makeText(M3U8VodDownloadActivity.this, getString(R.string.download_fail), Toast.LENGTH_SHORT) .show(); getBinding().setStateStr(getString(R.string.start)); @@ -211,7 +208,7 @@ public class M3U8DownloadActivity extends BaseActivity { void taskComplete(DownloadTask task) { if (task.getKey().equals(mUrl)) { getBinding().setProgress(100); - Toast.makeText(M3U8DownloadActivity.this, getString(R.string.download_success), + Toast.makeText(M3U8VodDownloadActivity.this, getString(R.string.download_success), Toast.LENGTH_SHORT).show(); getBinding().setStateStr(getString(R.string.re_start)); getBinding().setSpeed(""); @@ -221,7 +218,7 @@ public class M3U8DownloadActivity extends BaseActivity { @Override protected int setLayoutId() { - return R.layout.activity_m3u8; + return R.layout.activity_m3u8_vod; } public void onClick(View view) { @@ -241,7 +238,7 @@ public class M3U8DownloadActivity extends BaseActivity { } private void startD() { - Aria.download(M3U8DownloadActivity.this) + Aria.download(M3U8VodDownloadActivity.this) .load(mUrl) .useServerFileName(true) .setFilePath(mFilePath, true) @@ -252,7 +249,7 @@ public class M3U8DownloadActivity extends BaseActivity { return mUrl.substring(0, index + 1) + bandWidthUrl; } }) - .setTsUrlConvert(new ITsUrlConverter() { + .setTsUrlConvert(new IVodTsUrlConverter() { @Override public List convert(String m3u8Url, List tsUrls) { int index = m3u8Url.lastIndexOf("/"); String parentUrl = m3u8Url.substring(0, index + 1); @@ -267,12 +264,6 @@ public class M3U8DownloadActivity extends BaseActivity { .start(); } - @Override - protected void onStop() { - super.onStop(); - //Aria.download(this).unRegister(); - } - @Override protected void dataCallback(int result, Object data) { super.dataCallback(result, data); if (result == ModifyUrlDialog.MODIFY_URL_DIALOG_RESULT) { diff --git a/app/src/main/java/com/arialyy/simple/core/download/m3u8/M3U8Module.java b/app/src/main/java/com/arialyy/simple/core/download/m3u8/M3U8VodModule.java similarity index 92% rename from app/src/main/java/com/arialyy/simple/core/download/m3u8/M3U8Module.java rename to app/src/main/java/com/arialyy/simple/core/download/m3u8/M3U8VodModule.java index c9f9f98d..73162a9a 100644 --- a/app/src/main/java/com/arialyy/simple/core/download/m3u8/M3U8Module.java +++ b/app/src/main/java/com/arialyy/simple/core/download/m3u8/M3U8VodModule.java @@ -28,13 +28,13 @@ import com.arialyy.frame.base.BaseViewModule; import com.arialyy.simple.util.AppUtil; import java.io.File; -public class M3U8Module extends BaseViewModule { +public class M3U8VodModule extends BaseViewModule { private final String M3U8_URL_KEY = "M3U8_URL_KEY"; private final String M3U8_PATH_KEY = "M3U8_PATH_KEY"; // m3u8测试集合:http://www.voidcn.com/article/p-snaliarm-ct.html //private final String defUrl = "https://www.gaoya123.cn/2019/1557993797897.m3u8"; // 多码率地址: - private final String defUrl = "http://devimages.apple.com/iphone/samples/bipbop/bipbopall.m3u8"; + private final String defUrl = "http://ivi.bupt.edu.cn/hls/cctv6hd.m3u8"; private final String filePath = Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOWNLOADS).getPath() + "/1557993797897.ts"; @@ -54,10 +54,10 @@ public class M3U8Module extends BaseViewModule { singDownloadInfo = new DownloadEntity(); singDownloadInfo.setUrl(url); File temp = new File(this.filePath); - singDownloadInfo.setDownloadPath(filePath); + singDownloadInfo.setFilePath(filePath); singDownloadInfo.setFileName(temp.getName()); } else { - AppUtil.setConfigValue(context, M3U8_PATH_KEY, singDownloadInfo.getDownloadPath()); + AppUtil.setConfigValue(context, M3U8_PATH_KEY, singDownloadInfo.getFilePath()); AppUtil.setConfigValue(context, M3U8_URL_KEY, singDownloadInfo.getUrl()); } liveData.postValue(singDownloadInfo); @@ -78,7 +78,7 @@ public class M3U8Module extends BaseViewModule { File temp = new File(filePath); AppUtil.setConfigValue(context, M3U8_PATH_KEY, filePath); singDownloadInfo.setFileName(temp.getName()); - singDownloadInfo.setDownloadPath(filePath); + singDownloadInfo.setFilePath(filePath); liveData.postValue(singDownloadInfo); } diff --git a/app/src/main/java/com/arialyy/simple/modlue/CommonModule.java b/app/src/main/java/com/arialyy/simple/modlue/CommonModule.java index 95cd320d..0470e0ae 100644 --- a/app/src/main/java/com/arialyy/simple/modlue/CommonModule.java +++ b/app/src/main/java/com/arialyy/simple/modlue/CommonModule.java @@ -77,7 +77,8 @@ public class CommonModule extends BaseViewModule { R.drawable.ic_ftp, R.drawable.ic_ftp_dir, R.drawable.ic_ftp, - R.drawable.ic_ts + R.drawable.ic_ts, + R.drawable.ic_live }; int i = 0; for (String title : titles) { diff --git a/app/src/main/res/drawable/ic_live.xml b/app/src/main/res/drawable/ic_live.xml new file mode 100644 index 00000000..7df57e95 --- /dev/null +++ b/app/src/main/res/drawable/ic_live.xml @@ -0,0 +1,12 @@ + + + + diff --git a/app/src/main/res/layout/activity_m3u8_live.xml b/app/src/main/res/layout/activity_m3u8_live.xml new file mode 100644 index 00000000..97510f96 --- /dev/null +++ b/app/src/main/res/layout/activity_m3u8_live.xml @@ -0,0 +1,112 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + +