为了账号安全,请及时绑定邮箱和手机立即绑定

FastAPI前端视频流媒体的几种方法

标签:
Python API 直播

你可以在 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

运行上述命令启动服务器之后,前端出现了以下回复。

结果页

结论部分

我们使用了StreamingResponseFileResponse类直接在浏览器上播放本地和网络视频,同时我们也使用StreamingResponse流媒体播放了多个视频,这些视频是通过浏览器上的HTML文件渲染的。

本文中,我们使用了互联网上的视频网址,不过你也可以从数据库、本地文件、磁盘等处获取网址。

就这样吧.

继续写代码✌✌。

点击查看更多内容
TA 点赞

若觉得本文不错,就分享一下吧!

评论

作者其他优质文章

正在加载中
  • 推荐
  • 评论
  • 收藏
  • 共同学习,写下你的评论
感谢您的支持,我会继续努力的~
扫码打赏,你说多少就多少
赞赏金额会直接到老师账户
支付方式
打开微信扫一扫,即可进行扫码打赏哦
今天注册有机会得

100积分直接送

付费专栏免费学

大额优惠券免费领

立即参与 放弃机会
意见反馈 帮助中心 APP下载
官方微信

举报

0/150
提交
取消