Mediamtx+Python读取webrtc流

打印 上一主题 下一主题

主题 981|帖子 981|积分 2943

一、功能思绪:
  1. 1、我采用ffmpeg -re -stream_loop -1 -i xcc.mp4 -c:v libx264 -profile:v baseline -x264opts "bframes=0:repeat_headers=1" -b:v 1500k -preset fast -f flv rtmp://127.0.0.1:1835/stream/111推流到mediamtx的rtmp上
  2. 2、通过mediamtx自带的转流,我将可以直接把推过来的流以webrtc方式访问http://127.0.0.1:8889/stream/111
  3. 3、由于我确认http://127.0.0.1:8889/stream/111能够播放出视频,所以我现在想采用python的方式读取http://127.0.0.1:8889/stream/111/whep的流,来实现保存流的每帧图像
复制代码
涉及到的工具有:


  • 开源视频流服务器—>mediamtx
  • 本地推流rtmp工具—>ffmpeg+lal
  • 编写代码读取webrtc流工具—>yCharm

二、代码编写实现:
  1. import asyncio
  2. import json
  3. from aiortc import RTCPeerConnection, RTCSessionDescription, VideoStreamTrack
  4. import aiohttp
  5. import cv2
  6. import numpy as np
  7. class DummyVideoTrack(VideoStreamTrack):
  8.     """创建一个虚拟的视频轨道,以防止 WebRTC 报错"""
  9.     def __init__(self):
  10.         super().__init__()
  11.     async def recv(self):
  12.         # 生成一个黑色帧,防止 WebRTC 报错
  13.         width, height = 640, 480
  14.         frame = np.zeros((height, width, 3), dtype=np.uint8)
  15.         return frame
  16. class VideoReceiver:
  17.     def __init__(self):
  18.         self.frame = None
  19.     async def consume_track(self, track):
  20.         while True:
  21.             try:
  22.                 frame = await track.recv()
  23.                 img = frame.to_ndarray(format="bgr24")
  24.                 cv2.imshow("Video", img)
  25.                 cv2.waitKey(1)
  26.             except Exception as e:
  27.                 print("处理帧错误:", e)
  28.                 break
  29. async def send_ice_candidate(candidate, ice_url):
  30.     async with aiohttp.ClientSession() as session:
  31.         async with session.post(
  32.             ice_url,
  33.             data=json.dumps({"candidate": candidate}),
  34.             headers={"Content-Type": "application/json"}
  35.         ) as response:
  36.             if response.status != 200:
  37.                 print(f"发送 ICE candidate 错误: {response.status}")
  38. async def run():
  39.     pc = RTCPeerConnection()
  40.     receiver = VideoReceiver()
  41.     # 添加一个虚拟的视频轨道,防止 WebRTC 报错
  42.     pc.addTrack(DummyVideoTrack())
  43.     # 处理接收到的媒体轨道
  44.     def on_track(track):
  45.         print(f"接收到 {track.kind} 轨道")
  46.         if track.kind == "video":
  47.             asyncio.create_task(receiver.consume_track(track))
  48.     pc.add_listener("track", on_track)
  49.     # 监听 ICE 连接状态变化
  50.     def on_ice_connection_state_change():
  51.         print(f"ICE 连接状态: {pc.iceConnectionState}")
  52.     pc.on("iceconnectionstatechange", on_ice_connection_state_change)
  53.     # 监听 ICE Candidate
  54.     def on_ice_candidate(candidate):
  55.         if candidate:
  56.             print(f"新 ICE candidate: {candidate}")
  57.             asyncio.create_task(send_ice_candidate(candidate, ice_url))
  58.     pc.on("icecandidate", on_ice_candidate)
  59.     # 创建 SDP Offer
  60.     offer = await pc.createOffer()
  61.     await pc.setLocalDescription(offer)
  62.     # 发送 Offer 到 WHEP 端点
  63.     whep_url = "http://127.0.0.1:8889/stream/111/whep"
  64.     headers = {"Content-Type": "application/sdp"}
  65.     async with aiohttp.ClientSession() as session:
  66.         async with session.post(
  67.                 whep_url,
  68.                 data=pc.localDescription.sdp,
  69.                 headers=headers
  70.         ) as response:
  71.             if response.status != 201:
  72.                 raise Exception(f"服务器返回错误: {response.status}")
  73.             answer_sdp = await response.text()
  74.             answer = RTCSessionDescription(sdp=answer_sdp, type="answer")
  75.             await pc.setRemoteDescription(answer)
  76.             if "Location" in response.headers:
  77.                 ice_url = "http://127.0.0.1:8889" + response.headers["Location"]
  78.                 print("ICE 协商 URL:", ice_url)
  79.         try:
  80.             while True:
  81.                 await asyncio.sleep(1)
  82.         except KeyboardInterrupt:
  83.             pass
  84.         finally:
  85.             await pc.close()
  86. if __name__ == "__main__":
  87.     asyncio.run(run())
复制代码
三、效果展示:


  • 浏览器可以直接播放webrtc的扩展地址,然后最上层就是代码cv2读取每帧表现出来的画面


免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!更多信息从访问主页:qidao123.com:ToB企服之家,中国第一个企服评测及商务社交产业平台。

本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有账号?立即注册

x
回复

使用道具 举报

0 个回复

倒序浏览

快速回复

您需要登录后才可以回帖 登录 or 立即注册

本版积分规则

万万哇

金牌会员
这个人很懒什么都没写!
快速回复 返回顶部 返回列表