如何将WebRTC/aiortc直播流传入Python实现人脸识别?部署问题求助
解决方案:将客户端直播视频流传入Python人脸识别服务
1. 修复aiortc的MediaPlayer错误
你碰到的AttributeError是因为MediaPlayer不属于aiortc根模块,它在aiortc.contrib.media子模块下,正确用法如下:
from aiortc.contrib.media import MediaPlayer # 注意:该代码仅用于测试服务器本地相机,远程部署时你需要处理的是客户端浏览器的相机流,而非服务器本地设备 video = MediaPlayer('/dev/video0', format='v4l2', options={'video_size':'640x480'})
2. 核心问题拆解:远程部署时相机归属逻辑
远程访问场景下,服务器端的OpenCV无法直接获取用户相机权限——相机属于用户本地的浏览器设备,并非服务器资源。你需要通过WebRTC将客户端实时视频流传输到服务器,再在服务器端对接人脸识别逻辑。
端到端实现方案(Flask + aiortc)
服务器端(异步Flask服务)
- 安装依赖:
pip install flask aiortc opencv-python-headless face-recognition
- 编写服务代码:
from flask import Flask, render_template, request, jsonify from aiortc import RTCPeerConnection, RTCSessionDescription import cv2 import face_recognition import asyncio import numpy as np app = Flask(__name__) @app.route('/') def index(): return render_template('index.html') @app.route('/offer', methods=['POST']) async def offer(): params = await request.get_json() offer = RTCSessionDescription(sdp=params['sdp'], type=params['type']) pc = RTCPeerConnection({ 'iceServers': [{'urls': 'stun:stun.l.google.com:19302'}] }) @pc.on('track') def on_track(track): if track.kind == 'video': async def process_frames(): async for frame in track: # 将aiortc帧转换为OpenCV兼容格式 img = frame.to_ndarray(format='bgr24') # 执行人脸识别逻辑 face_locations = face_recognition.face_locations(img) # 可选:标记人脸(若需返回处理后的帧给客户端,可扩展MediaStreamTrack逻辑) for (top, right, bottom, left) in face_locations: cv2.rectangle(img, (left, top), (right, bottom), (0, 255, 0), 2) asyncio.create_task(process_frames()) @track.on('ended') def on_ended(): print(f"Track {track.kind} disconnected") await pc.setRemoteDescription(offer) answer = await pc.createAnswer() await pc.setLocalDescription(answer) return jsonify({ 'sdp': pc.localDescription.sdp, 'type': pc.localDescription.type }) if __name__ == '__main__': import uvicorn uvicorn.run(app, host='0.0.0.0', port=5000)
客户端(HTML + JS)
在templates目录下创建index.html,实现相机获取与WebRTC连接:
<!DOCTYPE html> <html> <head> <title>Face Recognition Service</title> </head> <body> <video id="localVideo" autoplay playsinline width="640" height="480"></video> <script> const localVideo = document.getElementById('localVideo'); // 获取本地相机流 async function getCameraStream() { return await navigator.mediaDevices.getUserMedia({ video: { width: 640, height: 480 }, audio: false }); } // 建立WebRTC连接 async function connectToServer(stream) { const pc = new RTCPeerConnection({ iceServers: [{ urls: 'stun:stun.l.google.com:19302' }] }); // 添加本地视频轨道到连接 stream.getTracks().forEach(track => pc.addTrack(track, stream)); // 创建Offer并发送至服务器 const offer = await pc.createOffer(); await pc.setLocalDescription(offer); const response = await fetch('/offer', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ sdp: pc.localDescription.sdp, type: pc.localDescription.type }) }); const answer = await response.json(); await pc.setRemoteDescription(new RTCSessionDescription(answer)); } // 启动服务 getCameraStream().then(stream => { localVideo.srcObject = stream; connectToServer(stream); }); </script> </body> </html>
3. 部署配置注意事项
- Nginx配置:需支持WebSocket(WebRTC信令依赖),示例配置片段:
server { listen 80; server_name your-domain.com; location / { proxy_pass http://127.0.0.1:5000; proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; # 开启WebSocket支持 proxy_http_version 1.1; proxy_set_header Upgrade $http_upgrade; proxy_set_header Connection "upgrade"; } }
- Gunicorn启动:由于使用异步代码,需指定异步Worker:
gunicorn -w 4 -k uvicorn.workers.UvicornWorker app:app --bind 0.0.0.0:5000
4. 轻量化替代方案:HTTP帧传输(低实时性场景)
若WebRTC实现复杂,可采用JS定时捕获视频帧,通过HTTP POST发送到服务器。示例客户端JS:
async function sendFrame() { const canvas = document.createElement('canvas'); canvas.width = 640; canvas.height = 480; const ctx = canvas.getContext('2d'); ctx.drawImage(localVideo, 0, 0, 640, 480); const frameData = canvas.toDataURL('image/jpeg'); await fetch('/process-frame', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ frame: frameData }) }); setTimeout(sendFrame, 200); // 每200ms发送一帧 }
服务器端处理代码:
import base64 from io import BytesIO from PIL import Image import numpy as np @app.route('/process-frame', methods=['POST']) def process_frame(): data = request.get_json() frame_bytes = base64.b64decode(data['frame'].split(',')[1]) img = Image.open(BytesIO(frame_bytes)).convert('RGB') img_cv = cv2.cvtColor(np.array(img), cv2.COLOR_RGB2BGR) # 人脸识别逻辑 face_count = len(face_recognition.face_locations(img_cv)) return jsonify({'face_count': face_count})
该方案实现简单,但延迟较高,适合对实时性要求较低的场景。
内容的提问来源于stack exchange,提问作者Dancun Gerald
相关产品推荐
相关产品推荐

