• 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();
        }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11

    文件下载

    文件下载使用的开源框架Filedownloader

     implementation 'com.liulishuo.filedownloader:library:1.7.7'//download
    
    • 1

    参数一:下载地址
    参数二:文件存储地址
    参数三:回调
    从外部传入需要的下载参数,然后通过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();
        }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33

    下载调用以及文件解析调用

    此处建立文件下载文件夹以及解析完成的文件夹地址,然后通过调用上述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();
                }
            });
        }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31

    解析

    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
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37

    外部引用

    参数一:下载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);
                }
            });
        }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31

    转换

    转换SRT字幕文件

    通过将本地的SRT字幕文件转为相对应集合实体数据,具体实体类型根据SRT文件内容而定

     public static List getSrtInfoList(String srtPath){
            List 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;
        }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84
    • 85
    • 86
    • 87
    • 88
    • 89

    获取SRT文件list实体数据

    通过以上步骤之后,即可将SRT文件转为相对应的list实体数据,最后与视频声音进行同步即可达到字幕与声音同步的效果

            String outPath = FileUtils.getInstance().createDirectory("OutputDirectory");
            String path = outPath +"/" +titleBeanList.get(curSubTitlePos).getT_name();
            srtEntityList.addAll(SrtParser.getSrtInfoList(path));
    
    • 1
    • 2
    • 3

    显示

    字幕显示

    然后通过获取字幕文件的片段的开始时间与结束时间,若当前视频的播放进度在此范围之内,即显示字幕,否则继续寻找;

    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;
                }
            }
        }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14

    若用户往前拖动视频进度条,则将字幕文件片段下标置为0,从头开始匹配

     if (currentPosition - curProgress < 0){
                                //seek --
                                curSubTitleNum = 0;
                            }
    
    • 1
    • 2
    • 3
    • 4
  • 相关阅读:
    干货 | 一文搞定 uiautomator2 自动化测试工具使用
    阿里云 Oss 权限控制
    MyEclipse,一个支持技术全面的现代Web开发工具
    mybatis config 配置
    三个步骤搞定 MySQL,offer到手。
    定时拉取k8s服务中日志
    QT 在主机默认PDF查看应用中打开PDF文档(如通过菜单栏打开使用手册)
    简单中间件模型
    XSS工具
    c语言变长数组的实现
  • 原文地址:https://blog.csdn.net/News53231323/article/details/126017080