|
|
|
@ -16,6 +16,7 @@ |
|
|
|
|
package com.arialyy.aria.m3u8; |
|
|
|
|
|
|
|
|
|
import android.text.TextUtils; |
|
|
|
|
|
|
|
|
|
import com.arialyy.aria.core.download.DTaskWrapper; |
|
|
|
|
import com.arialyy.aria.core.download.DownloadEntity; |
|
|
|
|
import com.arialyy.aria.core.download.M3U8Entity; |
|
|
|
@ -23,6 +24,7 @@ import com.arialyy.aria.core.listener.IEventListener; |
|
|
|
|
import com.arialyy.aria.core.loader.AbsNormalLoader; |
|
|
|
|
import com.arialyy.aria.util.ALog; |
|
|
|
|
import com.arialyy.aria.util.FileUtil; |
|
|
|
|
|
|
|
|
|
import java.io.BufferedReader; |
|
|
|
|
import java.io.File; |
|
|
|
|
import java.io.FileInputStream; |
|
|
|
@ -33,111 +35,123 @@ import java.io.InputStreamReader; |
|
|
|
|
import java.nio.charset.Charset; |
|
|
|
|
|
|
|
|
|
public abstract class BaseM3U8Loader extends AbsNormalLoader<DTaskWrapper> { |
|
|
|
|
protected M3U8TaskOption mM3U8Option; |
|
|
|
|
|
|
|
|
|
public BaseM3U8Loader(DTaskWrapper wrapper, IEventListener listener) { |
|
|
|
|
super(wrapper, listener); |
|
|
|
|
mM3U8Option = (M3U8TaskOption) wrapper.getM3u8Option(); |
|
|
|
|
mTempFile = new File(wrapper.getEntity().getFilePath()); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
@Override protected long delayTimer() { |
|
|
|
|
return 1000; |
|
|
|
|
} |
|
|
|
|
protected M3U8TaskOption mM3U8Option; |
|
|
|
|
|
|
|
|
|
/** |
|
|
|
|
* 获取ts文件保存路径 |
|
|
|
|
* |
|
|
|
|
* @param dirCache 缓存目录 |
|
|
|
|
* @param threadId ts文件名 |
|
|
|
|
*/ |
|
|
|
|
public static String getTsFilePath(String dirCache, int threadId) { |
|
|
|
|
return String.format("%s/%s.ts", dirCache, threadId); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
public String getCacheDir() { |
|
|
|
|
String cacheDir = mM3U8Option.getCacheDir(); |
|
|
|
|
if (TextUtils.isEmpty(cacheDir)) { |
|
|
|
|
cacheDir = FileUtil.getTsCacheDir(getEntity().getFilePath(), mM3U8Option.getBandWidth()); |
|
|
|
|
public BaseM3U8Loader(DTaskWrapper wrapper, IEventListener listener) { |
|
|
|
|
super(wrapper, listener); |
|
|
|
|
mM3U8Option = (M3U8TaskOption) wrapper.getM3u8Option(); |
|
|
|
|
mTempFile = new File(wrapper.getEntity().getFilePath()); |
|
|
|
|
} |
|
|
|
|
if (!new File(cacheDir).exists()) { |
|
|
|
|
FileUtil.createDir(cacheDir); |
|
|
|
|
|
|
|
|
|
@Override |
|
|
|
|
protected long delayTimer() { |
|
|
|
|
return 1000; |
|
|
|
|
} |
|
|
|
|
return cacheDir; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
/** |
|
|
|
|
* 创建索引文件 |
|
|
|
|
*/ |
|
|
|
|
public boolean generateIndexFile(boolean isLive) { |
|
|
|
|
File tempFile = |
|
|
|
|
new File(String.format(M3U8InfoTask.M3U8_INDEX_FORMAT, getEntity().getFilePath())); |
|
|
|
|
if (!tempFile.exists()) { |
|
|
|
|
ALog.e(TAG, "源索引文件不存在"); |
|
|
|
|
return false; |
|
|
|
|
/** |
|
|
|
|
* 获取ts文件保存路径 |
|
|
|
|
* |
|
|
|
|
* @param dirCache 缓存目录 |
|
|
|
|
* @param threadId ts文件名 |
|
|
|
|
*/ |
|
|
|
|
public static String getTsFilePath(String dirCache, int threadId) { |
|
|
|
|
return String.format("%s/%s.ts", dirCache, threadId); |
|
|
|
|
} |
|
|
|
|
FileInputStream fis = null; |
|
|
|
|
FileOutputStream fos = null; |
|
|
|
|
try { |
|
|
|
|
String cacheDir = getCacheDir(); |
|
|
|
|
fis = new FileInputStream(tempFile); |
|
|
|
|
fos = new FileOutputStream(getEntity().getFilePath()); |
|
|
|
|
BufferedReader reader = new BufferedReader(new InputStreamReader(fis)); |
|
|
|
|
String line; |
|
|
|
|
int i = 0; |
|
|
|
|
while ((line = reader.readLine()) != null) { |
|
|
|
|
byte[] bytes; |
|
|
|
|
if (line.startsWith("#EXTINF")) { |
|
|
|
|
fos.write(line.concat("\r\n").getBytes(Charset.forName("UTF-8"))); |
|
|
|
|
String tsPath = getTsFilePath(cacheDir, mRecord.threadRecords.get(i).threadId); |
|
|
|
|
bytes = tsPath.concat("\r\n").getBytes(Charset.forName("UTF-8")); |
|
|
|
|
reader.readLine(); // 继续读一行,避免写入源索引文件的切片地址
|
|
|
|
|
i++; |
|
|
|
|
} else if (line.startsWith("#EXT-X-KEY")) { |
|
|
|
|
M3U8Entity m3U8Entity = getEntity().getM3U8Entity(); |
|
|
|
|
String keyInfo = String.format("#EXT-X-KEY:METHOD=%s,URI=\"%s\",IV=%s\r\n", m3U8Entity.method, |
|
|
|
|
m3U8Entity.keyPath, m3U8Entity.iv); |
|
|
|
|
bytes = keyInfo.getBytes(Charset.forName("UTF-8")); |
|
|
|
|
} else { |
|
|
|
|
bytes = line.concat("\r\n").getBytes(Charset.forName("UTF-8")); |
|
|
|
|
} |
|
|
|
|
fos.write(bytes, 0, bytes.length); |
|
|
|
|
} |
|
|
|
|
// 直播的索引文件需要在结束的时候才写入结束标志
|
|
|
|
|
if (isLive) { |
|
|
|
|
fos.write("#EXT-X-ENDLIST".concat("\r\n").getBytes(Charset.forName("UTF-8"))); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
fos.flush(); |
|
|
|
|
return true; |
|
|
|
|
} catch (FileNotFoundException e) { |
|
|
|
|
e.printStackTrace(); |
|
|
|
|
} catch (IOException e) { |
|
|
|
|
e.printStackTrace(); |
|
|
|
|
} finally { |
|
|
|
|
try { |
|
|
|
|
if (fis != null) { |
|
|
|
|
fis.close(); |
|
|
|
|
} |
|
|
|
|
if (fos != null) { |
|
|
|
|
fos.close(); |
|
|
|
|
public String getCacheDir() { |
|
|
|
|
String cacheDir = mM3U8Option.getCacheDir(); |
|
|
|
|
if (TextUtils.isEmpty(cacheDir)) { |
|
|
|
|
cacheDir = FileUtil.getTsCacheDir(getEntity().getFilePath(), mM3U8Option.getBandWidth()); |
|
|
|
|
} |
|
|
|
|
if (tempFile.exists()) { |
|
|
|
|
FileUtil.deleteFile(tempFile); |
|
|
|
|
if (!new File(cacheDir).exists()) { |
|
|
|
|
FileUtil.createDir(cacheDir); |
|
|
|
|
} |
|
|
|
|
} catch (IOException e) { |
|
|
|
|
e.printStackTrace(); |
|
|
|
|
} |
|
|
|
|
return cacheDir; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
return false; |
|
|
|
|
} |
|
|
|
|
/** |
|
|
|
|
* 创建索引文件 |
|
|
|
|
*/ |
|
|
|
|
public boolean generateIndexFile(boolean isLive) { |
|
|
|
|
File tempFile = |
|
|
|
|
new File(String.format(M3U8InfoTask.M3U8_INDEX_FORMAT, getEntity().getFilePath())); |
|
|
|
|
if (!tempFile.exists()) { |
|
|
|
|
ALog.e(TAG, "源索引文件不存在"); |
|
|
|
|
return false; |
|
|
|
|
} |
|
|
|
|
FileInputStream fis = null; |
|
|
|
|
FileOutputStream fos = null; |
|
|
|
|
try { |
|
|
|
|
String cacheDir = getCacheDir(); |
|
|
|
|
fis = new FileInputStream(tempFile); |
|
|
|
|
fos = new FileOutputStream(getEntity().getFilePath()); |
|
|
|
|
BufferedReader reader = new BufferedReader(new InputStreamReader(fis)); |
|
|
|
|
String line; |
|
|
|
|
int i = 0; |
|
|
|
|
while ((line = reader.readLine()) != null) { |
|
|
|
|
byte[] bytes; |
|
|
|
|
if (line.startsWith("#EXTINF")) { |
|
|
|
|
fos.write(line.concat("\r\n").getBytes(Charset.forName("UTF-8"))); |
|
|
|
|
String tsPath = getTsFilePath(cacheDir, mRecord.threadRecords.get(i).threadId); |
|
|
|
|
bytes = tsPath.concat("\r\n").getBytes(Charset.forName("UTF-8")); |
|
|
|
|
reader.readLine(); // 继续读一行,避免写入源索引文件的切片地址
|
|
|
|
|
i++; |
|
|
|
|
} else if (line.startsWith("#EXT-X-KEY")) { |
|
|
|
|
M3U8Entity m3U8Entity = getEntity().getM3U8Entity(); |
|
|
|
|
StringBuffer stringBuffer = new StringBuffer("#EXT-X-KEY:"); |
|
|
|
|
//这里还原原M3U8文件的加密方式,URI,IV等
|
|
|
|
|
if (!TextUtils.isEmpty(m3U8Entity.method)) { |
|
|
|
|
//加密方式
|
|
|
|
|
stringBuffer.append("METHOD=").append(m3U8Entity.method); |
|
|
|
|
} else if (!TextUtils.isEmpty(m3U8Entity.keyPath)) { |
|
|
|
|
//key地址
|
|
|
|
|
stringBuffer.append(",URI=").append(m3U8Entity.keyPath); |
|
|
|
|
} else if (TextUtils.isEmpty(m3U8Entity.iv)) { |
|
|
|
|
//向量
|
|
|
|
|
stringBuffer.append(",IV=").append(m3U8Entity.iv).append("\r\n"); |
|
|
|
|
} |
|
|
|
|
bytes = stringBuffer.toString().getBytes(Charset.forName("UTF-8")); |
|
|
|
|
} else { |
|
|
|
|
bytes = line.concat("\r\n").getBytes(Charset.forName("UTF-8")); |
|
|
|
|
} |
|
|
|
|
fos.write(bytes, 0, bytes.length); |
|
|
|
|
} |
|
|
|
|
// 直播的索引文件需要在结束的时候才写入结束标志
|
|
|
|
|
if (isLive) { |
|
|
|
|
fos.write("#EXT-X-ENDLIST".concat("\r\n").getBytes(Charset.forName("UTF-8"))); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
fos.flush(); |
|
|
|
|
return true; |
|
|
|
|
} catch (FileNotFoundException e) { |
|
|
|
|
e.printStackTrace(); |
|
|
|
|
} catch (IOException e) { |
|
|
|
|
e.printStackTrace(); |
|
|
|
|
} finally { |
|
|
|
|
try { |
|
|
|
|
if (fis != null) { |
|
|
|
|
fis.close(); |
|
|
|
|
} |
|
|
|
|
if (fos != null) { |
|
|
|
|
fos.close(); |
|
|
|
|
} |
|
|
|
|
if (tempFile.exists()) { |
|
|
|
|
FileUtil.deleteFile(tempFile); |
|
|
|
|
} |
|
|
|
|
} catch (IOException e) { |
|
|
|
|
e.printStackTrace(); |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
@Override public long getCurrentProgress() { |
|
|
|
|
return isRunning() ? getStateManager().getCurrentProgress() : getEntity().getCurrentProgress(); |
|
|
|
|
} |
|
|
|
|
return false; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
protected DownloadEntity getEntity() { |
|
|
|
|
return mTaskWrapper.getEntity(); |
|
|
|
|
} |
|
|
|
|
@Override |
|
|
|
|
public long getCurrentProgress() { |
|
|
|
|
return isRunning() ? getStateManager().getCurrentProgress() : getEntity().getCurrentProgress(); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
protected DownloadEntity getEntity() { |
|
|
|
|
return mTaskWrapper.getEntity(); |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|