在本教程中,我们将探索如何将 Flask、OpenCV 和 WebRTC 简单而强大地结合起来,创建一个流媒体直播应用程序。无论您是初学者还是经验丰富的开发人员,本教程都将一步步指导您创建自己的流媒体直播平台。
要求
机器:任何笔记本电脑/PC 都可用于运行服务器。请注意,我们不会使用任何外部服务器,而是使用 Flask 创建自己的服务器。我们还需要一台可以使用网络浏览器观看直播的机器。这台机器可以是智能手机、不同的笔记本/电脑、远程机器(连接到同一网络)。
摄像头: Webcam / IP camera。
Python 软件包: 需要安装以下软件包。我们将使用 pip。
- Flask:用于构建一个简单的 Web 应用。
pip install Flask
- OpenCV:包含处理视频帧的工具。
pip install opencv-python-headless
- WebRTC:为 Web 应用程序添加实时流功能。我们将直接使用结合了 WebRTC 和 asyncio 的 aiortc python 软件包。
pip install aiortc
开始编码
创建一个目录,名称自定。我们将把项目分为三部分:网页(templates/index.html)、客户端(static/main.js)和服务器端(server.py)。下面是最终的目录层次结构:
your_app
├── static
│ └── main.js
├── templates
│ └── index.html
└── server.py
网页
在这里,我们将为网页创建一个简单的 HTML 模板。您可以自行添加任何花哨的样式,但我们不会这么做。在 your_app/templates 中创建 index.html,并添加以下代码。
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Your APP</title>
</head>
<body>
<video id="remoteVideo" autoplay></video>
<a href="{{ url_for('video_feed') }}" target="_self">Link to Video Feed</a>
<script src="{{ url_for('static', filename='main.js') }}"></script>
</body>
</html>
注意:我创建的网页不是直接在网页浏览器上打开实时流,而是提供了一个简单的链接 “Link to Video Feed
“,必须点击该链接,然后才能进入实时流,因为一般情况下,您希望在索引页上提供多种服务。但是,如果您想直接观看直播流,请转到下文服务器端部分。
客户端
客户端逻辑将使用 JavaScript 处理。我们将使用网络浏览器来查看我们的直播流,WebRTC API 可通过浏览器本地使用。在 your_app/static 中创建一个 main.js 文件。
如果觉得无聊,可点击此处查看完整的 main.js 文件
- RTCPeerConnection
let pc = new RTCPeerConnection();
这一行创建了 RTCPeerConnection 类的新实例,该类是 WebRTC 的基本组件,用于建立点对点连接。
- createOffer Function
async function createOffer() {
console.log("Sending offer request");
const offerResponse = await fetch("/offer", {
method: "POST",
headers: {
"Content-Type": "application/json",
},
body: JSON.stringify({
sdp: "",
type: "offer",
}),
});
const offer = await offerResponse.json();
console.log("Received offer response:", offer);
await pc.setRemoteDescription(new RTCSessionDescription(offer));
const answer = await pc.createAnswer();
await pc.setLocalDescription(answer);
}
该异步函数启动与服务器的要约交换流程。它会向服务器的”/offer “端点发送一个 POST 请求,并附上一个空的 SDP(会话描述协议)和要约类型。收到服务器的要约响应后,它会根据收到的要约设置远程描述,并创建一个应答。应答被设置为本地描述。
- 启动流程
createOffer();
通过创建并向服务器发送报价启动流程。
服务器端
该服务器设置了一个 Flask 网络应用程序,其中包含用于渲染 HTML 模板、处理 WebRTC 报文交换和从摄像头流式传输视频帧的路由。它使用 aiortc 实现 WebRTC 功能,并集成 OpenCV 以生成视频帧。在 your_app 中创建 server.py,并添加以下代码。
完整的 server.py 可在此处找到。
- 导入模块
from flask import Flask, render_template, Response, request, jsonify
from aiortc import RTCPeerConnection, RTCSessionDescription
import cv2
import json
import uuid
import asyncio
import logging
import time
- Flask 设置
app = Flask(__name__, static_url_path='/static')
创建带有静态 URL 路径的 Flask 应用程序实例,用于提供静态文件。
- 视频帧生成
def generate_frames():
camera = cv2.VideoCapture(0) # ==============IMPORTANT============
while True:
start_time = time.time()
success, frame = camera.read()
if not success:
break
else:
ret, buffer = cv2.imencode('.jpg', frame)
frame = buffer.tobytes()
# concat frame one by one and show result
yield (b'--frame\r\n'
b'Content-Type: image/jpeg\r\n\r\n' + frame + b'\r\n')
elapsed_time = time.time() - start_time
logging.debug(f"Frame generation time: {elapsed_time} seconds")
VideoCapture() 会触发摄像头并读取帧。
注意:如果要使用webcam(网络摄像头),请在参数中输入 0(零)。如果要使用 IP camera,则需要将 RTSP 链接作为参数传递。请阅读此文了解如何找到 RTSP 链接,但它应该如下所示(包括参数中的引号):
'rtsp://[USER]:[PASS]@[IP_ADDRESS]:[RTSP PORT]/media/video[STREAM TYPE]'
- 渲染 HTML 模板的路径
@app.route( '/' )
def index ():
return render_template( 'index.html' )
注意:该逻辑将带您进入 index.html,在那里您会找到链接到视频源的链接,必须点击该链接才能查看实时流。不过,如果您想直接转到视频源,可以修改上述逻辑,如下所示:
from flask import redirect, url_for
# Route to video_feed directly
@app.route('/')
def index():
return redirect(url_for('video_feed'))
- 异步报价处理
async def offer_async():
params = await request.json
offer = RTCSessionDescription(sdp=params["sdp"], type=params["type"])
# Create an RTCPeerConnection instance
pc = RTCPeerConnection()
# Generate a unique ID for the RTCPeerConnection
pc_id = "PeerConnection(%s)" % uuid.uuid4()
pc_id = pc_id[:8]
# Create and set the local description
await pc.createOffer(offer)
await pc.setLocalDescription(offer)
# Prepare the response data with local SDP and type
response_data = {"sdp": pc.localDescription.sdp, "type": pc.localDescription.type}
return jsonify(response_data)
async_offer()从 JSON 请求中提取参数、创建 RTCPeerConnection、生成唯一 ID 并设置名为 chat 的数据通道。创建、设置本地要约并准备响应数据。
- 运行异步报价函数的封装函数
def Offer ():
Loop = asyncio.new_event_loop()
asyncio.set_event_loop(loop)
future = asyncio.run_coroutine_threadsafe(offer_async(), Loop)
return future.result()
offer() 是一个封装函数,用于使用 asyncio 运行异步 offer_async() 函数。
- Offer Route
# Route to handle the offer request
@app.route('/offer', methods=['POST'])
def offer_route():
return offer()
- Video Feed Route
@app.route('/video_feed')
def video_feed():
return Response(generate_frames(), mimetype='multipart/x-mixed-replace; boundary=frame')
- 运行 Flask App
if __name__ == "__main__" :
app.run(debug= True , host= '0.0.0.0' )
注意:设置 host=’0.0.0.0’将允许您在不同机器的 Web 浏览器中查看流媒体。但机器(客户端)应连接到同一网络。默认情况下,Flask 将绑定到 localhost(127.0.0.1),也就是说,如果不按上图设置,您只能在运行服务器的机器的 Web 浏览器上查看数据流。
开始直播
要启动服务器,请按照以下步骤操作:
- 导航至应用程序所在的目录。在该目录下打开命令提示符/终端,输入 cd your_app,然后按回车键。
- 启用 webcam: 如果要使用 webcam,请确保启用摄像头访问。进入 “设置” > “隐私与安全” > “摄像头设置” > “启用摄像头访问”。如果你使用的是不同的操作系统,请遵循类似的设置。
- 如果没有Web浏览器,请安装任何一款。
- 在终端/命令提示符下键入:python server.py 或 python3 server.py(如果未设置别名)。
要在同一台机器的浏览器中查看视频流,请按住 Ctrl 键并点击 http://127.0.0.1:5000,这将在默认浏览器中打开网络摄像头视频流。
- 设置端口
请注意,5000 是 Flask 服务器监听的默认端口。您可以通过修改行设置端口参数来更改端口:
if __name__ == "__main__":
app.run(debug=True, host='0.0.0.0', port=8000) # put any port number
确保没有其他服务在新端口上运行,以避免冲突。要检查这一点,请执行以下操作:
在 Windows 上 打开命令提示符并键入:
netstat -ano | findstr :<port_number>
如果没有输出,则说明该端口上没有运行任何程序。但是,如果看到输出,请在输出的最后一列找到进程 ID。键入以下命令杀死进程或使用其他端口:
taskkill /F /PID <process_id>
在 Linux 上打开终端并输入:
sudo netstat -tuln | grep :<port_number>
同样,如果有进程正在运行,需要将其终止。
sudo kill <process_id>
- 在不同的客户端机器上查看直播
正如你在上面的输出(运行服务器)图片中看到的,你会得到两个链接来查看直播。一个是本地主机 http://127.0.0.1:5000,另一个是你的服务器 IP(为保护隐私,我已将其隐藏)。看起来是这样的
http://<flask_server_ip>:5000
转到另一台客户端机器的浏览器,可以是智能手机或另一台电脑,然后键入第二个链接(带服务器 IP)。然后就可以在浏览器中看到直播了。
要关闭流媒体,只需关闭客户端浏览器。要停止服务器,只需按下 Ctrl + C 即可。
注意事项
- 如上所述,客户端机器应与服务器机器连接到同一网络(同一无线路由器或以太网等)。
- 确保服务器上的防火墙设置允许在 Flask 应用程序中使用的端口上传入连接。
参考:GitHub 上的代码
作者:supersjgk
本文来自作者投稿,版权归原作者所有。如需转载,请注明出处:https://www.nxrte.com/jishu/webrtc/43200.html