研究:画像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 カメラ認証情報は環境変数や安全な設定ファイルで管理。
© 2025 Reiji Takashide. All rights reserved.

コメントを残す

メールアドレスが公開されることはありません。 が付いている欄は必須項目です

CAPTCHA