HTML5 视频跳过 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,end - start);

            HttpHeaders responseHeaders = new HttpHeaders();
            responseHeaders.set("Content-Range",String.format("%s %d-%d/%d",type,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>

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

解决方法

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