Android SRT字幕文件基础操作讲解
简介
需要在视频播放时,同步显示字幕,市面上主流的字幕文件一般为SRT文件,一般流程为:从服务器请求一个url地址,此为zip字幕压缩文件,一般需要请求url,下载zip文件,解析zip文件得到字幕srt文件,最后进行显示
下载
请求就不在此多言了,每个服务器请求体,返回题各异,没有参考价值。
下载zip文件我们需要在本地创建一个本地文件夹用来存储此文件。
创建文件夹
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
参数一:下载地址
参数二:文件存储地址
参数三:回调
从外部传入需要的下载参数,然后通过callback回调出去,进行页面更新操作
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(); }
下载调用以及文件解析调用
此处建立文件下载文件夹以及解析完成的文件夹地址,然后通过调用上述filedownloader文件下载回调,然后在下载完成的回调中进行文件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文件解析
此处被上述调用,用于zip文件解析
参数一:需要被解析的zip文件地址
参数二:输出文件夹地址
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 }
外部引用
参数一:下载url地址
参数二:存储文件夹名称
参数三:callback
上述zip文件下载以及zip文件解析为一个封装类,此处为在外部传入参数,通过回调进行页面更新,然后在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); } }); }
转换
转换SRT字幕文件
通过将本地的SRT字幕文件转为相对应集合实体数据,具体实体类型根据SRT文件内容而定
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实体数据
通过以上步骤之后,即可将SRT文件转为相对应的list实体数据,最后与视频声音进行同步即可达到字幕与声音同步的效果
String outPath = FileUtils.getInstance().createDirectory("OutputDirectory"); String path = outPath +"/" +titleBeanList.get(curSubTitlePos).getT_name(); srtEntityList.addAll(SrtParser.getSrtInfoList(path));
显示
字幕显示
然后通过获取字幕文件的片段的开始时间与结束时间,若当前视频的播放进度在此范围之内,即显示字幕,否则继续寻找;
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; }
到此这篇关于Android SRT字幕文件基础操作讲解的文章就介绍到这了,更多相关Android SRT字幕文件内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!
相关文章
Android 重写ViewGroup 分析onMeasure()和onLayout()方法
这篇文章主要介绍了Android 重写ViewGroup 分析onMeasure()和onLayout()方法的相关资料,需要的朋友可以参考下2017-06-06Android自定义Seekbar滑动条 Pop提示跟随滑动按钮滑动
这篇文章主要为大家详细介绍了Android自定义Seekbar滑动条,Pop提示跟随滑动按钮滑动,文中示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下2018-07-07
最新评论