你可以在 GeekPython 上找到这篇文章:GeekPython
FastAPI 是一个快速且现代的 Web 框架,以其对异步 REST API 的支持和使用方便而著称。
本文将介绍如何在 FastAPI 的前端播放视频。
流式响应对象播放本地视频
FastAPI 提供了一个专门用于流处理的 StreamingResponse
类。StreamingResponse
类会接收一个生成器或迭代器,并将其内容以流的形式传输。
这里有一个简单的示例,展示了如何将本地视频流媒体传输到浏览器。
# localvid.py
from fastapi import FastAPI
from fastapi.responses import StreamingResponse
app = FastAPI()
# 视频路径
vid_path = 'sample_video.mp4'
# 用于流式传输本地视频的函数
def stream_local_video():
with open(vid_path, 'rb') as vid_file:
yield from vid_file
# 流式传输视频的接口
@app.get("/")
def video_stream():
# 返回一个流式响应,媒体类型为 mp4 视频
return StreamingResponse(stream_local_video(), media_type='video/mp4')
我们从 fastapi.responses 模块导入了 StreamingResponse 类,这个操作挺常见的。
在 video_stream()
路径操作函数中,我们使用 StreamingResponse
来返回响应对象。我们将生成器函数 stream_local_video()
以及 media_type
作为参数传递给该类 StreamingResponse
。
生成器函数 stream_local_video()
读取视频的字节流,而 yield from
则逐个遍历这些字节,每次遍历的部分都会被 yield。
下面这个命令将启动服务器并在该地址流媒体播放视频。
> fastapi dev localvid.py
╭────────── FastAPI CLI - 开发模式 ───────────╮
│ │
│ 地址: http://127.0.0.1:8000 │
│ │
│ API 文档: http://127.0.0.1:8000/docs │
│ │
│ 运行在开发模式下,生产环境请使用: │
│ │
│ 运行 fastapi │
│ │
╰────────────────────────────────────────────╯
在线观看视频
# onlinevid.py
from fastapi import FastAPI
from fastapi.responses import StreamingResponse
import requests
app = FastAPI()
# 视频 URL
vid_url = 'https://cdn.pixabay.com/video/2023/07/28/173530-849610807_large.mp4'
# 流式播放在线视频的函数
def stream_online_video(url):
response = requests.get(url, stream=True)
for portion in response.iter_content(chunk_size=1024*1024):
yield portion # yield 产生
# 流式播放视频的接口
@app.get("/")
def video_stream():
return StreamingResponse(stream_online_video(vid_url), media_type='video/mp4')
在这个例子中,我们使用了 requests
库从 URL 下载视频流,并设置了 stream=True
,(避免一次性将视频读入内存,)然后我们迭代并分块返回视频内容(每次 1024 字节大小)。
当我们运行服务器时,它将从该URL传输视频内容。
我们可以在端点中指定URL,而不是使用硬编码的URL。
从 fastapi 导入 FastAPI 作为 FastAPI
从 fastapi 响应 导入 流式响应类
导入 requests
app = FastAPI()
# 定义一个函数来流式传输在线视频
def stream_online_video(url):
response = requests.get(url, stream=True)
for 部分 in response.iter_content(chunk_size=1024*1024):
yield 部分
# 流式传输视频
@app.get("/")
def video_stream(url):
返回 流式响应类(stream_online_video(url), media_type='video/mp4')
在此示例中,我们将 video_stream()
函数修改为可以接受一个 URL。
现在我们可以在端点里按如下格式指定视频 URL。
这是一个测试链接:http://127.0.0.1:8000/?url=https://cdn.pixabay.com/video/2023/07/28/173530-849610807_large.mp4
我们也可以通过在路径操作函数中使用async
关键字让其变为异步函数。
FileResponse
类只是将文件作为输入,并以流的形式返回响应。
from fastapi 导入 FastAPI
from fastapi.responses 导入 FileResponse
app = FastAPI()
# 视频流路径
vid_path = 'video.mp4'
@app.get("/")
def video_stream():
return FileResponse(vid_path, media_type='video/mp4')
在上述示例中,我们将视频文件传入 FileResponse
类并返回响应。
我们并没有像在处理 StreamingResponse
时那样逐字节读取和遍历视频数据来实现视频流媒体。
FileResponse
类非常适合处理相对较小或中等大小的文件,因为会将文件加载到内存中。对于大型文件,它们会消耗更多的内存。
在线观看视频
使用 FileResponse
从 URL 流式传输视频与流式传输本地视频不同。FileResponse
类会读取一个文件并流式传输它。
from fastapi import FastAPI
from fastapi.responses import FileResponse
import requests
import os
app = FastAPI()
# 视频 URL
vid_url = 'https://cdn.pixabay.com/video/2023/07/28/173530-849610807_large.mp4'
local_vid = 'sample_video.mp4'
# 从 URL 保存文件的函数
def save_as_file(url, path):
if not os.path.exists(path):
response = requests.get(url)
with open(path, 'wb') as vid:
vid.write(response.content)
save_as_file(vid_url, local_vid)
# 流式传输视频的请求处理函数
@app.get("/")
def video_stream():
return FileResponse(local_vid, media_type='video/mp4')
我们没有传输本地视频的流,而是使用 FileResponse
从 URL 传输了视频。
这样做不太好,因为我们先从 URL 下载视频到本地,然后再播放视频。
FileResponse
类最适合用来处理本地文件,而不是网页内容。
这主要是关于如何直接将视频流传输到浏览器的,但我们并不希望每次都要这样做,相反,我们希望视频能在我们网站的首页进行播放。
前端视频流那么,如何在前端用FastAPI流视频? 那我们用HTML创建前端,然后再把视频流起来。
首先,创建一个名为 serve_video
或你想要的任意目录名的目录,然后创建以下文件及子目录。
视频服务/
- 模板/
- 显示视频.html
- app.py
app.py
文件包含了我们的后端部分,.html 视频显示
文件包含了前端部分。
app.py
从 fastapi 导入 FastAPI, Request 作为 Request
从 fastapi.responses 导入 StreamingResponse, HTMLResponse
从 fastapi.templating 导入 Jinja2Templates 作为 Jinja2Templates
导入 requests
app = FastAPI()
templates = Jinja2Templates(directory="templates")
vid_urls = [
"https://cdn.pixabay.com/video/2023/07/28/173530-849610807_large.mp4",
'https://cdn.pixabay.com/video/2024/03/31/206294_large.mp4',
'https://cdn.pixabay.com/video/2023/10/11/184510-873463500_large.mp4',
'https://cdn.pixabay.com/video/2023/06/17/167569-837244635_large.mp4'
]
# 从 URL 流式传输视频的函数
def stream_video(url):
response = requests.get(url, stream=True)
for portion in response.iter_content(chunk_size=1024*1024):
yield portion
# 返回带有视频播放器的 HTML 模板的端点
@app.get("/", response_class=HTMLResponse)
async def video_template(request: Request):
return templates.TemplateResponse("display_video.html", {"request": request})
# 流式传输视频的端点,接受视频 ID
@app.get("/video/{video_id}")
async def video_stream(vid_id: int):
如果 0 <= vid_id < len(vid_urls):
return StreamingResponse(stream_video(vid_urls[vid_id]), media_type="video/mp4")
else:
返回 HTMLResponse("视频未找到", status_code=404)
在这个例子中,我们正在前端播放多个视频。我们在 vid_urls
中存储了视频的 URL 列表。
我们有一个 stream_video()
生成器,它将视频分成较小的块来流式传输。
我们创建了一个异步路径响应函数 video_template()
,它从 HTML 文件中读取并返回响应。
我们从templates
目录中的模板提供HTML,因此我们设置了Jinja2模板目录的位置(templates = Jinja2Templates(directory="templates")
)。其中HTML模板被存储,这就是FastAPI查找.html
文件的位置。
然后我们用templates.TemplateResponse("display_video.html", {"request": request})
来返回响应。我们传递了HTML模板display_video.html
,以及一个包含Request
对象({"request": request}
)的字典。其中,Request
对象包含了传入HTTP请求的相关信息,例如头部、cookies和URL,这些信息可以在HTML模板中被访问到。
然后,我们创建了一个端点(@app.get("/video/{video_id}")
)来根据视频ID流式传输单个视频。路径操作函数 video_stream(vid_id: int)
接受视频ID,该ID在 vid_urls
中。
在 video_stream()
函数中,if
条件判断 vid_id
是否在范围内,如果不是,则会抛出一个错误,否则,根据指定的索引进行视频流式传输。
display_video.html
<!DOCTYPE html>
<html lang="zh">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Stooooockzz</title>
</head>
<body>
<h1>我们提供什么?</h1>
<p>我们免费提供精彩的素材</p>
<h3>视频样本</h3>
<div style="display: flex; justify-content: space-evenly;">
<video width='20%' height='30%' style="border-radius: 10px;" controls autoplay>
<source class="lazyload" src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAAJcEhZcwAADsQAAA7EAZUrDhsAAAANSURBVBhXYzh8+PB/AAffA0nNPuCLAAAAAElFTkSuQmCC" data-original="/video/0" type="video/mp4">
</video>
<video width='20%' height='30%' style="border-radius: 10px;" controls autoplay>
<source class="lazyload" src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAAJcEhZcwAADsQAAA7EAZUrDhsAAAANSURBVBhXYzh8+PB/AAffA0nNPuCLAAAAAElFTkSuQmCC" data-original="/video/1" type="video/mp4">
</video>
<video width='20%' height='30%' style="border-radius: 10px;" controls autoplay>
<source class="lazyload" src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAAJcEhZcwAADsQAAA7EAZUrDhsAAAANSURBVBhXYzh8+PB/AAffA0nNPuCLAAAAAElFTkSuQmCC" data-original="/video/2" type="video/mp4">
</video>
<video width='20%' height='30%' style="border-radius: 10px;" controls autoplay>
<source class="lazyload" src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAAJcEhZcwAADsQAAA7EAZUrDhsAAAANSURBVBhXYzh8+PB/AAffA0nNPuCLAAAAAElFTkSuQmCC" data-original="/video/3" type="video/mp4">
</video>
</div>
</body>
</html>
此 HTML 文件中有多个 <video>
标签,源指向 /video/{video_id}
端点,根据 ID 流式传输视频的视频。
在这里,我们在代表从vid_urls
(在app.py
中)列表流式传输第一个视频的<video>
标签内插入了<source class="lazyload" src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAAJcEhZcwAADsQAAA7EAZUrDhsAAAANSURBVBhXYzh8+PB/AAffA0nNPuCLAAAAAElFTkSuQmCC" data-original="/video/0" type="video/mp4">
。同样地,我们为每个<video>
标签指定了相同的来源,但更改了每个标签内的ID值。
运行: fastapi dev app.py
运行上述命令启动服务器之后,前端出现了以下回复。
结果页
结论部分我们使用了StreamingResponse
和FileResponse
类直接在浏览器上播放本地和网络视频,同时我们也使用StreamingResponse
流媒体播放了多个视频,这些视频是通过浏览器上的HTML文件渲染的。
本文中,我们使用了互联网上的视频网址,不过你也可以从数据库、本地文件、磁盘等处获取网址。
就这样吧.
继续写代码✌✌。
共同学习,写下你的评论
评论加载中...
作者其他优质文章