Android SRT字幕文件基礎(chǔ)操作講解
簡介
需要在視頻播放時,同步顯示字幕,市面上主流的字幕文件一般為SRT文件,一般流程為:從服務(wù)器請求一個url地址,此為zip字幕壓縮文件,一般需要請求url,下載zip文件,解析zip文件得到字幕srt文件,最后進行顯示
下載
請求就不在此多言了,每個服務(wù)器請求體,返回題各異,沒有參考價值。
下載zip文件我們需要在本地創(chuàng)建一個本地文件夾用來存儲此文件。
創(chuàng)建文件夾
public String createDirectory(String name) {
File dir = new File(BaseApplication.getContext().getCacheDir(), name);
File file = BaseApplication.getContext().getExternalFilesDir(Environment.DIRECTORY_DOWNLOADS);
if (file != null) {
dir = new File(file, name);
}
if (!dir.exists()) {
dir.mkdirs();
}
return dir.toString();
}
文件下載
文件下載使用的開源框架Filedownloader
implementation 'com.liulishuo.filedownloader:library:1.7.7'//download
參數(shù)一:下載地址
參數(shù)二:文件存儲地址
參數(shù)三:回調(diào)
從外部傳入需要的下載參數(shù),然后通過callback回調(diào)出去,進行頁面更新操作
public void StartDownloadFile(String url,String path,FileDownloaderCallback callback){
FileDownloader.getImpl().create(url).setPath(path,true).setListener(new FileDownloadListener() {
@Override
protected void pending(BaseDownloadTask task, int soFarBytes, int totalBytes) {
callback.start();
}
@Override
protected void progress(BaseDownloadTask task, int soFarBytes, int totalBytes) {
}
@Override
protected void completed(BaseDownloadTask task) {
callback.completed(task.getPath());
}
@Override
protected void paused(BaseDownloadTask task, int soFarBytes, int totalBytes) {
}
@Override
protected void error(BaseDownloadTask task, Throwable e) {
callback.failed();
}
@Override
protected void warn(BaseDownloadTask task) {
}
}).start();
}
下載調(diào)用以及文件解析調(diào)用
此處建立文件下載文件夾以及解析完成的文件夾地址,然后通過調(diào)用上述filedownloader文件下載回調(diào),然后在下載完成的回調(diào)中進行文件zip解析
public void download(String url,String title,DownloadResultCallback callback){
String input = "InputDirectory";
String output = "OutputDirectory";
String inPath = FileUtils.getInstance().createDirectory(input);
String outPath = FileUtils.getInstance().createDirectory(output);
String sub = FileUtils.getInstance().createFile(inPath,"subTitleFile"+ File.separator);
DownloadUtils.getInstance().StartDownloadFile(url, sub, new DownloadUtils.FileDownloaderCallback() {
@Override
public void start() {
callback.downloadStart();
}
@Override
public void completed(String inputPath) {
callback.downloadSuccess();
String path = inputPath + "/" + title +".zip";
try {
ZipUtils.UnZipFolder(path,outPath);
callback.resolveSuccess();
} catch (Exception e) {
callback.resolveFailed(e.getMessage());
e.printStackTrace();
}
}
@Override
public void failed() {
callback.downloadFailed();
}
});
}
解析
ZIP文件解析
此處被上述調(diào)用,用于zip文件解析
參數(shù)一:需要被解析的zip文件地址
參數(shù)二:輸出文件夾地址
public class ZipUtils {
public static void UnZipFolder(String zipFileString, String outPathString)throws Exception {
java.util.zip.ZipInputStream inZip = new java.util.zip.ZipInputStream(new java.io.FileInputStream(zipFileString));
java.util.zip.ZipEntry zipEntry;
String szName = "";
while ((zipEntry = inZip.getNextEntry()) != null) {
szName = zipEntry.getName();
if (zipEntry.isDirectory()) {
// get the folder name of the widget
szName = szName.substring(0, szName.length() - 1);
java.io.File folder = new java.io.File(outPathString + java.io.File.separator + szName);
folder.mkdirs();
} else {
java.io.File file = new java.io.File(outPathString + java.io.File.separator + szName);
file.createNewFile();
// get the output stream of the file
java.io.FileOutputStream out = new java.io.FileOutputStream(file);
int len;
byte[] buffer = new byte[1024];
// read (len) bytes into buffer
while ((len = inZip.read(buffer)) != -1) {
// write (len) byte from buffer at the position 0
out.write(buffer, 0, len);
out.flush();
}
out.close();
}
}//end of while
inZip.close();
}//end o
}
外部引用
參數(shù)一:下載url地址
參數(shù)二:存儲文件夾名稱
參數(shù)三:callback
上述zip文件下載以及zip文件解析為一個封裝類,此處為在外部傳入?yún)?shù),通過回調(diào)進行頁面更新,然后在resolveSuccess()方法中進行異步操作(此方法代表zip文件被下載成功并且已被成功解析)
private void download(){
if (titleBeanList == null || titleBeanList.size() == 0)return;
if (curSubTitlePos == 0)return;
DownloadUtils.getInstance().download(titleBeanList.get(curSubTitlePos).getSub(), titleBeanList.get(curSubTitlePos).getT_name(), new DownloadUtils.DownloadResultCallback() {
@Override
public void downloadStart() {
Log.d(TAG,"download start");
}
@Override
public void downloadSuccess() {
Log.d(TAG,"download success");
}
@Override
public void downloadFailed() {
Log.d(TAG,"download fail");
}
@Override
public void resolveSuccess() {
Log.d(TAG,"resolve success");
handler.sendEmptyMessage(6);
}
@Override
public void resolveFailed(String failMsg) {
Log.d(TAG,"resolve error:"+failMsg);
}
});
}
轉(zhuǎn)換
轉(zhuǎn)換SRT字幕文件
通過將本地的SRT字幕文件轉(zhuǎn)為相對應(yīng)集合實體數(shù)據(jù),具體實體類型根據(jù)SRT文件內(nèi)容而定
public static List<SrtEntity> getSrtInfoList(String srtPath){
List<SrtEntity> srtList = new ArrayList<>();
try {
InputStreamReader read = new InputStreamReader(new FileInputStream(srtPath), "utf-8");
BufferedReader bufferedReader = new BufferedReader(read);
String textLine;
CursorStatus cursorStatus = CursorStatus.NONE;
SrtEntity entity = null;
while ((textLine = bufferedReader.readLine()) != null){
textLine = textLine.trim();
if (cursorStatus == CursorStatus.NONE) {
if (textLine.isEmpty()) {
continue;
}
if (!isNumeric(textLine)){
continue;
}
// New cue
entity = new SrtEntity();
// First textLine is the cue number
try {
entity.setNumber(Integer.parseInt(textLine));
} catch (NumberFormatException e) {
}
cursorStatus = CursorStatus.CUE_ID;
continue;
}
// Second textLine defines the start and end time codes
// 00:01:21,456 --> 00:01:23,417
if (cursorStatus == CursorStatus.CUE_ID) {
if (!textLine.substring(13, 16).equals("-->")) {
throw new Exception(String.format(
"Timecode textLine is badly formated: %s", textLine));
}
entity.setBg(parseTimeCode(textLine.substring(0, 12)));
entity.setEd(parseTimeCode(textLine.substring(17)));
cursorStatus = CursorStatus.CUE_TIMECODE;
continue;
}
// Following lines are the cue lines
if (!textLine.isEmpty() && (
cursorStatus == CursorStatus.CUE_TIMECODE ||
cursorStatus == CursorStatus.CUE_TEXT)) {
entity.addLine(textLine);
cursorStatus = CursorStatus.CUE_TEXT;
continue;
}
if (cursorStatus == CursorStatus.CUE_TIMECODE && textLine.isEmpty()) {
entity.addLine(textLine);
cursorStatus = CursorStatus.CUE_TEXT;
continue;
}
if (cursorStatus == CursorStatus.CUE_TEXT && textLine.isEmpty()) {
// End of cue
srtList.add(entity);
entity = null;
cursorStatus = CursorStatus.NONE;
continue;
}
}
} catch (UnsupportedEncodingException e) {
e.printStackTrace();
Log.e(TAG, e.getMessage());
} catch (FileNotFoundException e) {
e.printStackTrace();
Log.e(TAG, e.getMessage());
} catch (IOException e) {
e.printStackTrace();
Log.e(TAG, e.getMessage());
} catch (Exception e) {
e.printStackTrace();
Log.e(TAG, e.getMessage());
}
return srtList;
}獲取SRT文件list實體數(shù)據(jù)
通過以上步驟之后,即可將SRT文件轉(zhuǎn)為相對應(yīng)的list實體數(shù)據(jù),最后與視頻聲音進行同步即可達到字幕與聲音同步的效果
String outPath = FileUtils.getInstance().createDirectory("OutputDirectory");
String path = outPath +"/" +titleBeanList.get(curSubTitlePos).getT_name();
srtEntityList.addAll(SrtParser.getSrtInfoList(path));
顯示
字幕顯示
然后通過獲取字幕文件的片段的開始時間與結(jié)束時間,若當(dāng)前視頻的播放進度在此范圍之內(nèi),即顯示字幕,否則繼續(xù)尋找;
private void showSubTitle(){
if (srtEntityList == null || srtEntityList.size() == 0)return;
for (int i = curSubTitleNum; i < srtEntityList.size(); i++) {
long start = srtEntityList.get(i).getBg().getTime()+subtitleSpeed;
long end = srtEntityList.get(i).getEd().getTime()+subtitleSpeed;
if (curProgress >= start && curProgress <= end){
/**
* 字幕與進度相匹配*/
binding.VideoPlay.setSubTitle(srtEntityList.get(i).content.getText());
curSubTitleNum = i;
break;
}
}
}
若用戶往前拖動視頻進度條,則將字幕文件片段下標置為0,從頭開始匹配
if (currentPosition - curProgress < 0){
//seek --
curSubTitleNum = 0;
}
到此這篇關(guān)于Android SRT字幕文件基礎(chǔ)操作講解的文章就介紹到這了,更多相關(guān)Android SRT字幕文件內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
剖析Android Activity側(cè)滑返回的實現(xiàn)原理
在很多的App中,都會發(fā)現(xiàn)利用手指滑動事件,進行高效且人性化的交互非常有必要,那么它是怎么實現(xiàn)的呢,本文給大家解析實現(xiàn)原理,對Activity側(cè)滑返回實現(xiàn)代碼感興趣的朋友一起看看吧2021-06-06
Android 搜索結(jié)果匹配關(guān)鍵字且高亮顯示功能
這篇文章主要介紹了Android 搜索結(jié)果匹配關(guān)鍵字且高亮顯示功能,需要的朋友可以參考下2017-05-05
Android 重寫ViewGroup 分析onMeasure()和onLayout()方法
這篇文章主要介紹了Android 重寫ViewGroup 分析onMeasure()和onLayout()方法的相關(guān)資料,需要的朋友可以參考下2017-06-06
Android7.0以上Uri轉(zhuǎn)路徑的方法實現(xiàn)(已驗證)
這篇文章主要介紹了Android7.0以上Uri轉(zhuǎn)路徑的方法實現(xiàn),文中通過示例代碼介紹的非常詳細,對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2020-03-03
Android自定義Seekbar滑動條 Pop提示跟隨滑動按鈕滑動
這篇文章主要為大家詳細介紹了Android自定義Seekbar滑動條,Pop提示跟隨滑動按鈕滑動,文中示例代碼介紹的非常詳細,具有一定的參考價值,感興趣的小伙伴們可以參考一下2018-07-07
BroadcastReceiver靜態(tài)注冊案例詳解
這篇文章主要為大家詳細介紹了BroadcastReceiver靜態(tài)注冊案例,文中示例代碼介紹的非常詳細,具有一定的參考價值,感興趣的小伙伴們可以參考一下2022-08-08

