求助:通过Socket.IO将FFmpeg流从Python推送到浏览器显示视频
浏览器播放FFmpeg推送的视频流解决方案
一、调整Python后端脚本
原脚本的事件参数和推送逻辑有小问题,修改后可正确响应前端请求并推送流:
import socketio import subprocess import uvicorn sio = socketio.AsyncServer(async_mode='asgi', cors_allowed_origins='*') app = socketio.ASGIApp(sio) @sio.event async def connect(sid, environ): await sio.enter_room(sid, 'Streaming') print(f"客户端 {sid} 已连接") # 修改事件名称为start_stream,明确触发逻辑 @sio.event async def start_stream(sid): video_path = r'temp_server\result.avi' audio_path = r'temp_server\temp.wav' ffmpeg_command = [ 'ffmpeg', '-re', # 按视频实际帧率推送 '-i', video_path, '-i', audio_path, '-c:v', 'copy', '-c:a', 'aac', '-f', 'mpegts', '-mpegts_flags', 'resend_headers', # 每次连接重发TS头,避免客户端无法初始化 'pipe:1' ] try: # 同时捕获stderr方便排查FFmpeg错误 process = subprocess.Popen(ffmpeg_command, stdout=subprocess.PIPE, stderr=subprocess.PIPE) while True: data = process.stdout.read(4096) # 增大读取块提升传输效率 if not data: break # 推送到当前请求的客户端 await sio.emit('video_chunk', data, to=sid) except Exception as e: print(f"推流错误: {e}") finally: process.stdout.close() process.stderr.close() process.wait() print("推流结束") if __name__ == '__main__': try: uvicorn.run(app, host='0.0.0.0', port=8000) except KeyboardInterrupt: exit()
修改点说明:
- 重命名事件为
start_stream,更清晰表达前端触发逻辑 - 添加
-mpegts_flags resend_headers参数,确保客户端每次连接都能获取TS流头部,避免无法播放 - 增大读取块大小到4096字节,优化传输效率
- 完善进程资源清理和异常捕获
二、浏览器端HTML+JavaScript实现
创建index.html文件,通过socket.io-client连接后端,利用MediaSource API处理MPEG-TS流并播放:
<!DOCTYPE html> <html> <head> <title>FFmpeg流播放器</title> <style> video { width: 800px; height: auto; border: 1px solid #ccc; } </style> </head> <body> <video id="videoPlayer" controls autoplay muted></video> <script src="https://cdn.socket.io/4.7.2/socket.io.min.js"></script> <script> const video = document.getElementById('videoPlayer'); const socket = io('http://localhost:8000'); let mediaSource; let sourceBuffer; let bufferQueue = []; // 初始化媒体源 function initMediaSource() { mediaSource = new MediaSource(); video.src = URL.createObjectURL(mediaSource); mediaSource.addEventListener('sourceopen', () => { // MPEG-TS对应的MIME类型(适配H.264视频+AAC音频) const mimeCodec = 'video/mp2t; codecs="avc1.42E01E, mp4a.40.2"'; if (MediaSource.isTypeSupported(mimeCodec)) { sourceBuffer = mediaSource.addSourceBuffer(mimeCodec); sourceBuffer.mode = 'sequence'; // 按顺序追加数据 // 处理缓冲队列中待加载的数据 while (bufferQueue.length > 0 && !sourceBuffer.updating) { appendBuffer(bufferQueue.shift()); } } else { console.error('不支持该MIME类型:', mimeCodec); } }); } // 向媒体源追加数据 function appendBuffer(buffer) { try { sourceBuffer.appendBuffer(buffer); } catch (e) { console.error('追加缓冲失败:', e); } } // 缓冲更新完成后继续处理队列 mediaSource.addEventListener('updateend', () => { if (bufferQueue.length > 0 && !sourceBuffer.updating) { appendBuffer(bufferQueue.shift()); } }); // 连接成功后请求开始推流 socket.on('connect', () => { console.log('已连接到流媒体服务器'); initMediaSource(); socket.emit('start_stream'); }); // 接收视频流数据 socket.on('video_chunk', async (chunk) => { const buffer = await chunk.arrayBuffer(); if (sourceBuffer && !sourceBuffer.updating) { appendBuffer(buffer); } else { bufferQueue.push(buffer); } }); // 处理断开连接 socket.on('disconnect', () => { console.log('与服务器断开连接'); if (mediaSource) { mediaSource.endOfStream(); } }); </script> </body> </html>
前端代码说明:
- 使用
socket.io-client建立与后端的实时连接,监听video_chunk事件接收流数据 - 通过
MediaSourceAPI创建可动态追加数据的媒体源,适配MPEG-TS格式 - 维护缓冲队列,避免在sourceBuffer更新时直接追加数据导致错误
- 视频设置
muted是满足浏览器自动播放策略要求
三、运行步骤
- 安装Python依赖:
pip install python-socketio uvicorn - 确保FFmpeg已安装并添加到系统PATH
- 运行Python后端脚本
- 在浏览器中打开
index.html,即可观看推送的视频流
内容的提问来源于stack exchange,提问作者sam
相关产品推荐
相关产品推荐

