HTML5 视频在 32,768 之后跳过范围

HTML5 video skips ranges after 32,768

我正在使用 Java 探索 Http Range 请求和视频流。我想创建一个控制器,将视频流式传输到标签。

由于某种原因,在结束范围 32768 之后,浏览器发送请求开始 100237312。 这是我控制台的一部分日志:

...
Start: 27648, End: 28672, chunk size: 1024

Start: 28672, End: 29696, chunk size: 1024

Start: 29696, End: 30720, chunk size: 1024

Start: 30720, End: 31744, chunk size: 1024

Start: 31744, End: 32768, chunk size: 1024

Start: 100237312, End: 100238336, chunk size: 1024

Start: 100238336, End: 100239360, chunk size: 1024
...

我的代码:

package com.example.demo.controllers;

import org.springframework.core.io.InputStreamResource;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;

import java.io.*;
import java.nio.file.Files;
import java.util.Arrays;

@RestController
@RequestMapping("/video")
public class VideoCtrl {

    @CrossOrigin
    @ResponseBody
    @GetMapping
    public ResponseEntity<InputStreamResource> getVideo(@RequestHeader("Range") String range) {
        String[] rangeHeaderParams = HttpRange.parseHttpRangeHeader(range);
        File file = new File(getClass().getClassLoader().getResource("video_for_test.mp4").getFile());
        InputStream is = null;
        try {
            is = new FileInputStream(file);
        } catch (FileNotFoundException e) {
            e.printStackTrace();
        }
        long fileSize = file.length();
        assert is != null;

        final int CHUNK_SIZE = 1024;
        String type = rangeHeaderParams[0];
        int start = Integer.parseInt(rangeHeaderParams[1]);
        int end = start + CHUNK_SIZE;


        System.out.println(String.format(
                "Start: %d, End: %d, chunk size: %d\n", start, end, end - start
        ));

        byte[] chunk = new byte[CHUNK_SIZE];

        try {
            is.skip(start);
            is.read(chunk, 0, end - start);

            HttpHeaders responseHeaders = new HttpHeaders();
            responseHeaders.set("Content-Range", String.format("%s %d-%d/%d", type, start, end, fileSize));
            responseHeaders.set("Accept-Ranges", type);
            responseHeaders.set("Content-Length", String.format("%d", CHUNK_SIZE));
            responseHeaders.set("Content-Type", "video/mp4");
            responseHeaders.set("Connection", "keep-alive");
            responseHeaders.set("Content-Transfer-Encoding", "binary");
            responseHeaders.set("Cache-Control", "no-cache, no-store");
            responseHeaders.set("Expires", "0");
            responseHeaders.set("Keep-Alive", "timeout=100000 max=50");

            return new ResponseEntity<>(new InputStreamResource(new ByteArrayInputStream(chunk)), responseHeaders, HttpStatus.PARTIAL_CONTENT);
        } catch (IOException e) {
            return new ResponseEntity<>(new InputStreamResource(new ByteArrayInputStream("error".getBytes())), null, HttpStatus.BAD_REQUEST);
        }
    }
}

客户:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
    <video controls width="480">
        <source src="http://localhost:8080/video" type="video/mp4">
    </video>
</body>
</html>

如果有人解释整个概念,我将不胜感激,因为我没有看到它在实践中起作用。理论上,我应该 return 文件的一部分并发送结束范围,以便浏览器知道下一个 'start'.

是什么

MP4 文件并不像您认为的那样工作。你不只是从一开始就开始玩。有一个称为“moov box”的“索引”,它描述了“mdat box”中数据的布局。 moov 框可以位于文件的开头或结尾。在这种情况下,moov 可能位于偏移量 100237312。但浏览器必须下载文件的开头才能了解 moov 的位置。 moov 完全下载后,浏览器可以计算文件任何开始时间的字节范围偏移量。这就是寻求的方式。