研究:画像AIネット通信環境を考える(WebRTC)
2025年5月11日 | by Reiji Takashide
概要
多くの IP カメラは RTSP プロトコルで映像を配信していますが、ブラウザにはそのまま再生できません。
そこで Python の aiortc
ライブラリを使い、RTSP ストリームを受け取りつつ
WebRTC 形式に変換し、ブラウザにデリバリーするシンプルなリレーサーバを実装できたらという研究。実装途中なので何があっても責任は取れません。悪しからず。
要件
- Python 3.8 以上
- ライブラリ:
aiohttp
,aiortc
,opencv-python
- IP カメラの RTSP URL(例:
rtsp://camera_ip/stream
)
インストールコマンド:
pip install aiohttp aiortc opencv-python
サーバー実装 (server.py)
RTSP からフレームを取得し、WebRTC 用に変換してクライアントに送出します。
import asyncio
import cv2
from aiohttp import web
from aiortc import RTCPeerConnection, MediaStreamTrack
RTSP_URL = "rtsp:///stream"
pcs = set()
class RTSPVideoTrack(MediaStreamTrack):
kind = "video"
def __init__(self):
super().__init__()
self.cap = cv2.VideoCapture(RTSP_URL)
async def recv(self):
pts, time_base = await self.next_timestamp()
ret, frame = self.cap.read()
if not ret:
raise ConnectionError("RTSP stream ended")
# BGR → RGB
frame = cv2.cvtColor(frame, cv2.COLOR_BGR2RGB)
from av import VideoFrame
video_frame = VideoFrame.from_ndarray(frame, format="rgb24")
video_frame.pts = pts
video_frame.time_base = time_base
return video_frame
async def offer(request):
params = await request.json()
pc = RTCPeerConnection()
pcs.add(pc)
pc.addTrack(RTSPVideoTrack())
@pc.on("iceconnectionstatechange")
async def on_ice_state():
if pc.iceConnectionState == "failed":
await pc.close()
pcs.discard(pc)
await pc.setRemoteDescription(params["sdp"])
answer = await pc.createAnswer()
await pc.setLocalDescription(answer)
return web.json_response({
"sdp": pc.localDescription.sdp,
"type": pc.localDescription.type
})
async def on_shutdown(app):
coros = [pc.close() for pc in pcs]
await asyncio.gather(*coros)
pcs.clear()
app = web.Application()
app.on_shutdown.append(on_shutdown)
app.router.add_post("/offer", offer)
if __name__ == "__main__":
web.run_app(app, port=8080)
クライアント実装 (index.html)
ブラウザで RTCPeerConnection を立ち上げ、映像を受信して <video>
要素に渡します。
<!DOCTYPE html>
<html lang="ja">
<head>
<meta charset="UTF-8" />
<title>IP カメラ WebRTC ビューア</title>
</head>
<body>
<h1>IP カメラ映像</h1>
<video id="video" autoplay playsinline controls></video>
<script>
const pc = new RTCPeerConnection();
const video = document.getElementById("video");
pc.ontrack = ({ track, streams }) => {
if (track.kind === "video") {
video.srcObject = streams[0];
}
};
async function start() {
const offer = await pc.createOffer();
await pc.setLocalDescription(offer);
const res = await fetch("/offer", {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({ sdp: pc.localDescription })
});
const answer = await res.json();
await pc.setRemoteDescription(answer);
}
start().catch(console.error);
</script>
</body>
</html>
運用のヒント
- 低遅延を重視する場合は、GStreamer+
webrtcbin
や専用メディアサーバ(Janus, Kurento)を検討。 - NAT 越えのために STUN/TURN サーバを併用すると安定性が向上。
- IP カメラ認証情報は環境変数や安全な設定ファイルで管理。