自研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
相关产品推荐
相关产品推荐

