0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

MediaMTX を使って負荷テストをした話

Last updated at Posted at 2025-11-29

はじめに

とあるシステムの検証で、RTP over HTTP(RTSP over HTTP) の多セッション負荷を確認する必要が出てきました。

  • クライアント側は Axis カメラ互換の「HTTPトンネル」(GET/POST + X-Sessioncookie)のみ対応
  • サーバ側は OSS の MediaMTX を使って RTSP サーバを立てたい
  • 100〜300 セッション規模で負荷を見たい

という背景から、

「Go で Axis 互換の RTSP-over-HTTP プロキシを書いて、MediaMTX に中継しよう」

となり、ついでに 生成系AIをフル活用してツールを作ってみました。


全体構成

今回の構成はシンプルです。

録画サーバ/ビューア
    ↓  HTTP/1.0 (GET/POST, X-Sessioncookie)
rtsp-http-proxy (Go)
    ↓  RTSP/TCP
MediaMTX
    ↓  H.264 copy
MP4ファイル

ポイント

  • クライアントは Axis カメラ互換の HTTP トンネルで接続
  • 自作 rtsp-http-proxy が HTTP トンネルを終端して RTSP/TCP へ変換
  • MediaMTX は MP4 ファイルから H.264 を copy 配信(再エンコードなし)

要件整理

  • RTSP over HTTP(Axis スタイル)の再現
    • GET /axis-media/media.amp
    • POST /axis-media/media.amp
    • X-Sessioncookie でセッション識別
  • セッション管理
    • 1 セッション = 1本の RTSP/TCP コネクション
  • RTSP リクエストの書き換え
    • HTTPリクエストをRTSPに書き換え
    • OPTIONS rtsp://127.0.0.1:8554/axis-media/media.amp RTSP/1.0
  • メッセージ処理
    • Base64 をデコードして RTSP メッセージを MediaMTX に転送
    • MediaMTX からの RTSP/RTP バイト列をそのまま HTTP レスポンスとして中継

ChatGPTへの要求

以下のプロンプトを設定したコードを動作確認しながら仕上げました。

MediaMTX を RTSP サーバとして利用し、
Axis 互換の「RTSP over HTTP(HTTPトンネル)」を行うプロキシを Go で実装したいです。

【やりたいこと】
- クライアントは GET/POST /axis-media/media.amp + X-Sessioncookie でアクセス
- POST ボディは Base64 でエンコードされた RTSP メッセージ
- セッションは cookie 単位で管理し、上流には RTSP/TCP で中継する

【要件】
- RTSP リクエスト 1 行目の書き換え
- GET 側で MediaMTX からの RTP をそのまま HTTP 応答として流す
- YAML の config で listen / upstream / log を指定できるように

まず、全体構成の設計方針を文章で説明し、そのあと main.go のフルコードを出してください。

実装ポイント

セッション管理

セッションはcookie でマップしています。
受信したリクエストがcookieと紐づいて管理されるようになっています。

type session struct {
    cookie  string
    remote  string
    created time.Time

    rtspMu   sync.Mutex
    rtspConn net.Conn

    getMu                 sync.Mutex
    getWriter             http.ResponseWriter
    getFlusher            http.Flusher
    upstreamReaderStarted bool

    postMu  sync.Mutex
    b64Buf  []byte
    rtspBuf []byte

    closed bool
}

RTSPリクエストの書き換え

/axis-media/... を rtsp:///axis-media/... に変換します。

func rewriteRTSPRequest(req []byte) []byte {
    idx := bytesIndexCRLF(req)
    if idx <= 0 {
        return req
    }

    firstLine := string(req[:idx])
    rest := req[idx+2:]

    fields := strings.Split(firstLine, " ")
    if len(fields) < 3 {
        return req
    }

    method, uri, version := fields[0], fields[1], fields[2]

    if strings.HasPrefix(strings.ToLower(uri), "rtsp://") {
        return req
    }
    if strings.HasPrefix(uri, "/") {
        newURI := fmt.Sprintf("rtsp://%s%s", cfg.Upstream, uri)
        newFirst := fmt.Sprintf("%s %s %s", method, newURI, version)
        // ログなど省略…

        newReq := append([]byte(newFirst), '\r', '\n')
        newReq = append(newReq, rest...)
        return newReq
    }
    return req
}

MediaMTX側の設定

MediaMTX は、MP4 ファイルから H.264 をコピー配信する設定にしました。

paths:
  "axis-media/media.amp":
    source: file
    sourceOnDemand: yes
    sourceFile: /path/to/sample.mp4
    # H.264 copy 配信(再エンコードなし)

60秒で切断される問題と KeepAlive

最初は MediaMTX のログに、こんな感じのメッセージが出ていました。
約 60 秒ごとに RTSP コネクションが閉じられ、セッションも破棄されてしまいます。

[RTSP] [conn 127.0.0.1:51984] closed: read tcp 127.0.0.1:8554->127.0.0.1:51984: i/o timeout
[RTSP] [session XXXXXXXX] destroyed: not in use

対策として、、

  • プロキシ側から一定間隔で OPTIONS を送る KeepAlive を実装

に変更したところ、長時間安定してセッションを維持できるようになりました。

まとめ

  • Axis 互換の RTSP over HTTP を Go で再現し、MediaMTX に中継するプロキシを実装
  • 生成AIをうまく使うことで、要件整理 → 初回実装 → 不具合修正 → KeepAlive 対応までかなりのスピードで回すことができた
  • 次はHTTPSにも対応させたい
0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?