You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

React+Video.js对接Java后端,MP4视频无法在Safari播放求助

问题:Safari无法通过接口加载MP4视频,其他环境正常

技术栈

  • 前端:React + Video.js 7.20.1
  • 后端:Java 8

前端代码

const playFunc = ()=> {
    if (!player.src.src) {
        player.src({
            src: "http://192.168.0.86:8080/api/v1/" + "get-video/" + videoId,
            type: "video/mp4"
        });
    }
    player.play();
};

后端代码

@GetMapping(value = "/get-video/{video-id}", produces = "video/mp4")
public ResponseEntity<StreamingResponseBody> getVideo(@PathVariable("video-id") String videoId) throws IOException {
    String filePath = "HLC_Promo_-Small.mp4";
    byte[] bytes = Files.readAllBytes(Paths.get(filePath));
    return ResponseEntity
            .ok()
            .contentType(MediaType.valueOf("video/mp4"))
            .body(os -> os.write(bytes/*videoService.getVideo(videoId).getContent()*/));
}

测试情况

  • Chrome、安卓Chrome:正常播放
  • QuickTime:正常播放
  • 直接拖拽到Safari:正常播放
  • 通过接口加载到Safari:无法播放

问题原因及解决方法

1. 核心问题:缺少Range请求支持

Safari对视频流强制要求服务器支持Range分段请求(断点续传),当前后端一次性返回完整文件,未处理Range请求头,导致Safari无法正确解析视频流。

修改后端代码以支持Range请求:

@GetMapping(value = "/get-video/{video-id}", produces = "video/mp4")
public ResponseEntity<?> getVideo(@PathVariable("video-id") String videoId,
                                  @RequestHeader(value = "Range", required = false) String range) throws IOException {
    Path filePath = Paths.get("HLC_Promo_-Small.mp4");
    long fileSize = Files.size(filePath);

    // 处理无Range请求的情况
    if (range == null) {
        byte[] bytes = Files.readAllBytes(filePath);
        return ResponseEntity.ok()
                .contentType(MediaType.valueOf("video/mp4"))
                .header("Accept-Ranges", "bytes")
                .contentLength(fileSize)
                .body(bytes);
    }

    // 处理Range分段请求
    String[] rangeParts = range.replace("bytes=", "").split("-");
    long start = Long.parseLong(rangeParts[0]);
    long end = rangeParts.length > 1 ? Long.parseLong(rangeParts[1]) : fileSize - 1;

    // 校验Range合法性
    if (start > end || start >= fileSize) {
        return ResponseEntity.status(HttpStatus.REQUESTED_RANGE_NOT_SATISFIABLE)
                .header("Content-Range", "bytes */" + fileSize)
                .build();
    }

    long contentLength = end - start + 1;
    RandomAccessFile raf = new RandomAccessFile(filePath.toFile(), "r");
    raf.seek(start);
    byte[] bytes = new byte[(int) contentLength];
    raf.read(bytes);
    raf.close();

    return ResponseEntity.status(HttpStatus.PARTIAL_CONTENT)
            .contentType(MediaType.valueOf("video/mp4"))
            .header("Content-Range", "bytes " + start + "-" + end + "/" + fileSize)
            .contentLength(contentLength)
            .body(bytes);
}

2. 前端Video.js优化

避免直接调用play(),等待视频元数据加载完成后再播放,同时显式开启预加载:

const playFunc = ()=> {
    if (!player.src.src) {
        player.src({
            src: "http://192.168.0.86:8080/api/v1/get-video/" + videoId,
            type: "video/mp4",
            preload: 'auto'
        });
    }
    // 等待元数据加载完成后播放
    player.one('loadedmetadata', () => {
        player.play();
    });
};

3. 确认视频编码兼容性

确保视频采用Safari原生支持的H.264编码,可通过ffmpeg命令验证:

ffmpeg -i HLC_Promo_-Small.mp4

查看输出中Video:字段是否包含h264标识。

内容的提问来源于stack exchange,提问作者Сергей

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.21 08:36:22