Java下視頻彈幕的獲取和解析

原文地址

原文鏈接

前言

這里以bilibili的彈幕文件為例,彈幕文件一般為兩種,ass文件和xml文件,前者對于普通用戶更友好,后者對于程序員更友好。如果經常下載視頻資源的小伙伴應該知道,ass文件常常作為外掛字幕文件使用,同文件下一般視頻播放器會自動加載同名ass文件,可以通過這個直接實現(xiàn)彈幕播放,但是這個web端是不行,現(xiàn)在web播放器默認只支持webvtt文件作為字幕資源。扯遠了,我們這里以B站彈幕 + DPlayer彈幕源引擎的背景簡單說下,如何在自己個人網站實現(xiàn)彈幕播放效果

實現(xiàn)

彈幕獲取

獲取彈幕源的方式有很多,這個最簡單的還是使用瀏覽器插件,這個比較簡單就不多說了

彈幕上傳

本文使用的是xml解析的方式,如果小伙伴覺得ass文件解析更簡單那么使用它也是不錯的方法。由于我們的多媒體服務是反應式的,這里我們的示例代碼都是反應式的,大家需要稍作調整

    @PostMapping(value = "/your_path", consumes = MediaType.MULTIPART_FORM_DATA_VALUE)
    public Mono<Void> subBarInsert(@RequestPart("file") FilePart file,
                                   @PathVariable String videoId) {
        return barrageService.insertSubBarrageList(file, videoId);
    }
    @Override
    public Mono<Void> insertSubBarrageList(FilePart file, String videoId) {
        @SuppressWarnings("deprecation")
        Mono<byte[]> fileDataByte = DataBufferUtils.join(file.content())
                .map(dataBuffer -> dataBuffer.asByteBuffer().array());
        //save
        return fileDataByte.map(BarrageParseUtils::genBarrageList)
                .map(barrageDtosList -> {
                    VideoBarrageRef ref = new VideoBarrageRef();
                    //set data ...
                    return ref;
                })
                .flatMap(entity -> videoBarrageRefMapper.save(entity))
                .then();
    }

這里只是簡單示例,缺少很多穩(wěn)定性代碼,大家自行根據(jù)需求添加

下方為彈幕的實際解析,xml文件的解析參考本站Java解析Xml文件轉為數(shù)據(jù)對象,這里就不重復了。該工具類還進行了彈幕壓縮,不需要的小伙伴也可以去掉,本來應該在輸出時隨機壓縮彈幕的,但是考慮到性能問題改在彈幕上傳時候壓縮。一般比較火的視頻幾萬條彈幕是很正常的,所以如果在輸出時候進行隨機壓縮,小服務器是沒辦法很快響應的

public class BarrageParseUtils {

    public static final double MAX_BARRAGE_SIZE = 3000.0;

    public static final int ZIP_OFFSET = 10;

    public static final List<Integer> ZIP_INDEX = Arrays.asList(0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0);

    public static List<BarrageDto> genBarrageList(byte[] file) {
        List<BarrageDto> barrageDtoList = new ArrayList<>();
        try {
            //convert to obj
            XmlMapper xmlMapper = new XmlMapper();
            SubBarrageCollection poppy = xmlMapper.readValue(file, SubBarrageCollection.class);
            List<SubBarrage> subBarrageList = poppy.getSubBarrageList();
            //zip ratio
            double zipRatio = MAX_BARRAGE_SIZE / subBarrageList.size();
            zipRatio = zipRatio > 1 ? 1 : zipRatio;
            List<Integer> dynamicZipRate = ZIP_INDEX.subList(0, (int) (zipRatio * ZIP_OFFSET) + 1);
            //convert to entity
            for (SubBarrage subBarrage : subBarrageList) {
                try {
                    BarrageDto barrageDto = new BarrageDto();
                    barrageDto.setText(subBarrage.getContent());
                    String[] barrageDetail = subBarrage.getProperty().split(",");
                    barrageDto.setTime(Double.valueOf(barrageDetail[0]));
                    if (!dynamicZipRate.contains((int) (barrageDto.getTime() * ZIP_OFFSET) % ZIP_OFFSET)) {
                        continue;
                    }
                    barrageDto.setColor(Integer.valueOf(barrageDetail[3]));
                    //todo https://github.com/DIYgod/DPlayer/blob/master/src/js/danmaku.js 
                    barrageDto.setType(0);
                    barrageDto.setAuthor("");
                    barrageDtoList.add(barrageDto);
                } catch (Exception ignored) {
                }
            }
        } catch (Exception ex) {
            ex.printStackTrace();
        }
        return barrageDtoList;
    }
}

DPlayer的彈幕結構在本站的Vue3下構建帶有彈幕功能的Web播放器有介紹,同樣不再贅述,這里簡單給下后端實體類

public class BarrageDto {

    /**
     * video barrage time 5.312
     */
    private Double time;

    /**
     * 233333
     */
    private String text;

    /**
     * #fff
     */
    private Integer color;

    /**
     * barrage site
     */
    private Integer type;

    /**
     * author
     */
    private String author;
}

彈幕發(fā)放

這里沒啥,就按照前端數(shù)據(jù)結果直接取出來即可,這個給個簡易示例

    @Override
    public Flux<BarrageDto> getBarrageList(String videoId) {
        return videoBarrageRefMapper.getByVideoId(videoId)
                .map(data -> JSON.parseArray(data.getBarrageTextSub(), BarrageDto.class))
                .flatMapMany(Flux::fromIterable);
    }

原文地址

原文鏈接

?著作權歸作者所有,轉載或內容合作請聯(lián)系作者
【社區(qū)內容提示】社區(qū)部分內容疑似由AI輔助生成,瀏覽時請結合常識與多方信息審慎甄別。
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發(fā)布,文章內容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務。

相關閱讀更多精彩內容

友情鏈接更多精彩內容