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

自研Uvicorn视频服务脚本VLC无法播放,Chrome正常

问题:自定义Uvicorn视频服务无法在VLC播放,Starlette版本却正常

我编写了一个支持Range请求的Uvicorn视频服务脚本,该脚本在Chrome浏览器中可正常播放视频,但在VLC播放器中无法播放。此前参考网上已找不到的脚本,用Starlette编写了同类脚本,将同一视频通过该Starlette脚本部署在Uvicorn上时,VLC不仅能播放还支持拖拽跳转。

基础版Uvicorn服务脚本

import asyncio
from pathlib import Path

def get_header(headers, header):
    #returns header value if header present else None
    for h in headers:
        if h[0] == header:
            return h[1]
    return None


def get_bytes(path, start, end):
    #returns bytes from the file from start to end, both inclusive
    with open(path, 'rb+') as f:
        f.seek(start)
        return f.read(end+1-start)

KB=1024
MB=1024*KB

async def app(scope, recieve, send):

    file = Path('bbb.1080p.30fps.mp4')
    size = file.stat().st_size
    headers=[
        [b'content-type', b'video/mp4'],
        [b'accept-ranges', b'bytes'],
        [b'content-length', str(size).encode()]
    ]
    if range_header := get_header(scope['headers'], b'range'):
        range_header = range_header.decode().replace('bytes=', '').split('-')
        start = int(range_header[0])
        end = min(start+MB, size-1)
        body = get_bytes(file, start, end)
        status = 206
        headers[2][1]=str(len(body)).encode()
        headers.append([b'content-range', f'bytes {start}-{end}/{size}'.encode()])
    else:
        status = 200
        body = b''

    await send({
        'type':'http.response.start',
        'status':status,
        'headers':headers
    })
    await send({
        'type':'http.response.body',
        'more_body':True,
        'body':body
    })

Starlette版本脚本

import os
import asyncio

from starlette.applications import Starlette
from starlette.responses import StreamingResponse
from starlette.routing import Route

def stream_bytes(file_path, start, end, chunk_size = 1024*1024):
    with open(file_path, mode="rb") as f:
        f.seek(start)
        while (pos := f.tell()) <= end:
            read_size = min(chunk_size, end + 1 - pos)
            readdata = f.read(read_size)
            yield readdata

async def video(request):
    file_path = "bbb.1080p.30fps.mp4"
    file_size = os.path.getsize(file_path)
    range_header = request.headers.get("range")
    headers = {
        "content-type": "video/mp4",
        "accept-ranges": "bytes",
        "content-length": str(file_size),
    }

    start = 0
    end = file_size - 1
    status_code = 200

    if range_header is not None:
        h = range_header.replace("bytes=", "").split("-")
        start = int(h[0]) if h[0] != "" else 0
        end = int(h[1]) if h[1] != "" else file_size - 1
        size = end - start + 1
        headers["content-length"] = str(size)
        headers["content-range"] = f"bytes {start}-{end}/{file_size}"
        status_code = 206
        
    return StreamingResponse(stream_bytes(file_path, start, end), headers=headers, status_code=status_code)

app = Starlette(routes=[Route('/', video)])

排查情况

对比两个脚本的网络响应,发现完全一致。查看starlette/responses.py源码,仍未找到问题所在。

补充信息

将原代码中的拼写错误more-body修改为more_body后,Chrome仍可播放视频,但VLC依旧无法播放,且现在在Chrome和VLC中播放时都会出现错误:

ERROR:    ASGI callable returned without completing response.

内容的提问来源于stack exchange,提问作者Eclipse Kelby

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.11 04:33:11