Flask 部分视频流在 Ipad 和 Iphone 上不起作用

Flask Partial Video Stream not working on Ipad and Iphone

提问人:LennikGamez 提问时间:11/14/2023 最后编辑:LennikGamez 更新时间:11/18/2023 访问量:43

问:

我正在尝试编写一个 flask 网页,它将特定范围的字节流式传输到客户端,因此您可以从网络驱动器观看大型视频文件(mp4),而无需加载整个视频。它已经在我的笔记本电脑和托管网页的电脑上运行,但是当我尝试在苹果移动设备上使用该网页时,我经常遇到问题。视频只是没有加载。视频播放器保持空,不显示视频持续时间,并且无法启动和观看视频。

我已经尝试使用其他浏览器。我尝试了 Safari、Google 和 Firefox,但没有任何效果。

这是流式处理页面的 HTML:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>{{title}}</title>
</head>
<body>
    <video controls="true" width="650" playsinline autoplay muted loop>
        <source src="{{url_for('stream.getMovie',id=id, ver=version)}}" type="video/mp4">
    </video>
</body>
</html>

这是相关的 Flask 文件:

import mimetypes
from flask import Response, render_template, request, Blueprint
import os

from helper import getMovieById, getMovieList


stream = Blueprint("stream", __name__)


 
def removeNonDigit(str):
    final = ""
    for i in str:
        if i  in "0123456789":
            final += i
    return final






@stream.route("/movie/<id>/<ver>", methods=['GET'])
def getMovie(id, ver):
    range = request.headers.get("range")
    if not range:
        return Response("Requires Range header",status=400)
    
    videoPath: str = getMovieById(id).get("versions")[int(ver)][1]

    videoSize: bytes = os.stat(videoPath).st_size

    # parse Range
    # "bytes=3323-"
    CHUNK_SIZE = (10 ** 6)*1 # about 1 MB
    x = range[:range.find("-")]
    start = int(removeNonDigit(x))
    end = min(start + CHUNK_SIZE, videoSize-1)

    # read the data
    with open(videoPath, "rb") as v:
        v.seek(start)
        data = v.read(CHUNK_SIZE) 

    content_length = end - start + 1
    headers = {
        "Content-Range": f'bytes {start}-{end}/{videoSize}',
        "Accept-Ranges": "bytes",
        "Content-Length": content_length,
        "Content-Type": "video/mp4",
    }
    return Response(data, status=206, headers=headers, mimetype=mimetypes.guess_type(videoPath)[0],direct_passthrough=True)








@stream.route("/watch/<id>/<version>")
def player(id, version):
    return render_template("movie.html", title=getMovieById(id).get("name"), version=version, id=id)

@stream.route("/banner/<id>")
def banner(id):
    img = getMovieById(id).get("img")
    with open(img, "rb") as f:
        data = f.read()
    return Response(data, status=202)

这可能有点乱,我现在已经尝试了很长时间

这是我从 PC 连接但未开始视频时从 flask 控制台获得的输出:

127.0.0.1 - - [13/Nov/2023 20:14:02] "GET /watch/1/1 HTTP/1.1" 200 -
127.0.0.1 - - [13/Nov/2023 20:14:04] "GET /movie/1/1 HTTP/1.1" 206 -
127.0.0.1 - - [13/Nov/2023 20:14:04] "GET /movie/1/1 HTTP/1.1" 206 -

但是从我的 iPad 连接,我只得到这个输出:

192.xxx.xxx.xxx - - [13/Nov/2023 20:15:36] "GET /watch/1/0 HTTP/1.1" 200 -
192.xxx.xxx.xxx - - [13/Nov/2023 20:15:38] "GET /movie/1/0 HTTP/1.1" 206 -

我还发现 iPad 请求中的标头的字节范围为“bytes=0-1”,而 PC 的字节范围为“bytes=0-”

在请求方面,我不是专家,但也许这会有所帮助......

如果您需要更多详细信息或代码片段,请告诉我。

提前感谢:3

python ios flask httprequest http-live-streaming

评论

0赞 gre_gor 11/14/2023
你有没有读过 Range 标题实际上可以是什么?
0赞 LennikGamez 11/15/2023
@gre_gor对不起,我不太确定你的意思。但是我阅读了文档,没有发现任何有用的东西。我的 iPad 上的 Safari 浏览器支持 Range 标头,据我所知,我的 Response Range 标头的格式也是正确的。但就像我说的,我不确定你的真正意思......

答:

0赞 LennikGamez 11/18/2023 #1

因此,在花了越来越多的时间试图弄清楚发生了什么之后,我发现手机上的浏览器会发送一个测试请求,只要求咬一口,然后接受更大的范围。 我通过检查范围标头是否为“bytes=0-1”来修复它,如果是这样,我只返回浏览器请求的视频的一口,而不是发送回 1MB 的块。