介绍
在 Web 开发中,视频播放是一个常见的需求。HLS(HTTP Live Streaming)和 FLV(Flash Video)是两种常见的视频流媒体传输协议。以下是它们的详细介绍和实现方法。
FLV(Flash Video)
简介
-
FLV 是 Adobe 开发的视频格式,曾经广泛用于 Flash 播放器。
-
虽然 Flash 已被淘汰,但 FLV 格式仍然可以通过 HTML5 和 JavaScript 播放。
优点
-
文件体积较小,适合低带宽环境。
-
支持流式传输,延迟较低。
缺点
-
兼容性较差,现代浏览器不再支持 Flash。
-
需要额外的 JavaScript 库(如
flv.js
)来播放 FLV 视频。
后端
依赖
-
FastAPI:
-
用于构建 API 的 Web 框架。
-
安装命令:
pip install fastapi
-
-
Uvicorn:
-
用于运行 FastAPI 应用的 ASGI 服务器。
-
安装命令:
pip install uvicorn
-
代码
from fastapi import HTTPException, FastAPI, Query, Header
from fastapi.responses import JSONResponse, FileResponse
from fastapi.middleware.cors import CORSMiddleware
from fastapi.responses import StreamingResponse
from typing import Generator, Optional
import json
import os
import uvicorn
app = FastAPI()
# 添加CORS中间件
app.add_middleware(
CORSMiddleware,
allow_origins=["*"], # 允许所有来源
allow_credentials=True,
allow_methods=["*"], # 允许所有方法
allow_headers=["*"], # 允许所有头部
)
class BaseResponse:
def __init__(self, code, msg):
self.code = code
self.msg = msg
class MyFile:
def __init__(self, filename, knowledge_base_name):
self.filename = filename
self.filepath = os.path.join(knowledge_base_name, filename)
def iter_file(file_path: str, start: int = 0, end: Optional[int] = None) -> Generator[bytes, None, None]:
with open(file_path, "rb") as file:
file.seek(start)
while chunk := file.read(8192):
yield chunk
if end and file.tell() >= end:
break
@app.get("/knowledge_base/download_video")
def stream_video(
filename: str = Query(..., description="FLV文件名称", examples=["test.flv"]),
range: Optional[str] = Header(None)
):
"""
流式传输FLV视频,支持范围请求
"""
file_path = os.path.join(os.getcwd(), "files", filename)
if not os.path.exists(file_path):
raise HTTPException(status_code=404, detail="文件未找到")
file_size = os.path.getsize(file_path)
start = 0
end = file_size - 1
if range:
# Parse the range header
match = range.replace("bytes=", "").split("-")
start = int(match[0])
if len(match) > 1 and match[1]:
end = int(match[1])
content_length = end - start + 1
headers = {
"Content-Range": f"bytes {start}-{end}/{file_size}",
"Accept-Ranges": "bytes",
"Content-Length": str(content_length),
"Content-Type": "video/x-flv"
}
return StreamingResponse(
iter_file(file_path, start=start, end=end + 1),
status_code=206,
headers=headers,
media_type="video/x-flv"
)
def convert_json_to_vtt(json_file, vtt_file):
# Read JSON data from a file
with open(json_file, 'r', encoding='utf-8') as f:
json_data = json.load(f)
vtt_data = "WEBVTT\n\n"
for i, item in enumerate(json_data):
start_time = float(item['start'])
end_time = start_time + float(item['duration'])
start_time_min, start_time_sec = divmod(start_time, 60)
end_time_min, end_time_sec = divmod(end_time, 60)
vtt_data += f"{i+1}\n"
vtt_data += f"{int(start_time_min):02}:{start_time_sec:06.3f} --> {int(end_time_min):02}:{end_time_sec:06.3f}\n"
vtt_data += f"{item['text']}\n\n"
# Write VTT data to a file
with open(vtt_file, 'w', encoding='utf-8') as f:
f.write(vtt_data)
@app.get("/knowledge_base/download_vtt")
def download_vtt(
file_name: str = Query(..., description="文件名称"),
preview: bool = Query(False, description="是:浏览器内预览;否:下载")
):
"""
下载字幕文件
"""
vtt_dir = "./files/vtt_dir"
json_dir = "./files/vtt_dir"
name = os.path.splitext(os.path.basename(file_name))[0]
vtt_name = name + ".vtt"
vtt_file_path = vtt_dir + "/" +vtt_name
if not os.path.exists(vtt_file_path):
json_file_path = json_dir + "/" + name + ".json"
convert_json_to_vtt(json_file_path,vtt_file_path)
if preview:
content_disposition_type = "inline"
else:
content_disposition_type = None
try:
kb_file = MyFile(filename=vtt_name, knowledge_base_name=vtt_dir)
if os.path.exists(kb_file.filepath):
response = FileRespon