You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何将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服务)

  1. 安装依赖:
pip install flask aiortc opencv-python-headless face-recognition
  1. 编写服务代码:
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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.14 09:25:19