Руководства5 октября 2026 г. · 7 мин чтения

Нагрузочное тестирование WebRTC через настоящий медиа-путь — WHIP/WHEP, P2P-звонки, симулькаст

HTTP-инструменты останавливаются на сигналинге. Perfscale теперь гоняет полные ICE/DTLS-SRTP рукопожатия и настоящий RTP-трафик на каждого виртуального пользователя — WHIP-публикация, WHEP-просмотр, кастомный сигналинг и P2P-звонки между парами VU. Доступно на Scale и Enterprise.

Автор: Команда Perfscale

Нагрузочное тестирование WebRTC через настоящий медиа-путь — WHIP/WHEP, P2P-звонки, симулькаст

Нагружать WebRTC-систему HTTP-инструментами — это фикция: вы долбите сигнальный endpoint, пока самое дорогое — DTLS-криптография, RTP-форвардинг, джиттер-буферы на SFU — простаивает. Perfscale теперь гоняет полный медиа-путь: каждый виртуальный пользователь выполняет настоящие ICE/DTLS-SRTP рукопожатия и шлёт или принимает настоящий RTP — против вашего сервера или против другого VU.

WebRTC — премиальная возможность, доступная на планах Scale и Enterprise. Тесты с действиями pro/webrtc-* отклоняются при создании на Starter. Фича поставляется в агенте 0.4.0 и работает одинаково в perfscale CLI, агенте perfscaled и под управлением controlplane.

Три сценария, комбинируемые в одном тесте

  • WHIP/WHEP — стандартизированные endpoint'ы публикации/просмотра. Публикуйте синтетическое или файловое AV в WHIP-endpoint, смотрите WHEP-поток и измеряйте его.
  • Кастомный сигналинг — LiveKit, mediasoup, Janus, любой проприетарный протокол: wasm-библиотека получает SDP-оффер и возвращает ответ, так что вендорские особенности живут в версионируемых библиотеках, а не в движке.
  • P2P-звонки между парами VU — один композитный шаг выполняет весь звонок: пейринг → коннект → медиа в обе стороны → hold → статистика → закрытие. Сигнальный сервер не нужен.

Публикация в WHIP-endpoint

# test.yaml
steps:
  - name: publish camera
    use: pro/webrtc-connect@v1
    with:
      signal: whip
      url: https://stream.example.com/whip/cam-${vu}
      bearer: ${WHIP_TOKEN}
    outputs: cam

  - name: send media
    use: pro/webrtc-publish@v1
    with:
      id: ${cam.id}
      tracks:
        - kind: video
          codec: av1
          source: synthetic
          bitrate: 1500kbps
          resolution: 1280x720
        - kind: audio
          codec: opus
          source: synthetic
          bitrate: 64kbps

Синтетическое аудио — это тон Opus с дизерингом амплитуды (чтобы энкодер не сворачивался в DTX); синтетическое видео — движущийся тест-паттерн с вшитой меткой времени. Движок — чистый Rust, поэтому поведение resolution: зависит от кодека: AV1 кодируется по-настоящему через rav1e в запрошенном разрешении и битрейте — примерно одно ядро CPU на кодируемый трек, честная цена настоящего кодирования. VP8 и H.264 проигрывают встроенный ассет 320x240 (несовпадающий resolution: пишет предупреждение, а не врёт). source: file зацикливает ваши файлы: IVF (VP8/AV1), Annex-B H.264, Opus-in-Ogg.

Мультитрек и симулькаст

Реальные клиенты шлют больше одной камеры. Объявите раскладку на коннекте (в WHIP нет пересогласования), затем прикрепите треки на publish:

  - name: publish connect
    use: pro/webrtc-connect@v1
    with:
      signal: whip
      url: https://stream.example.com/whip/studio-${vu}
      tracks:                        # по умолчанию 1 аудио + 1 видео
        - { kind: audio }
        - { kind: video }            # камера
        - kind: video                # демонстрация экрана, симулькаст
          layers: [ { rid: f }, { rid: h }, { rid: q } ]
    outputs: studio

Слои симулькаста получают строки a=rid / a=simulcast в оффере, per-layer SSRC в проводе и per-layer метрики отправки (webrtc_video1_f_bitrate_bps). Одно честное ограничение: настоящий AV1 SVC (пространственные слои внутри одного потока) отклоняется с точечной ошибкой — у rav1e нет API пространственных слоёв, и мы это не подделываем.

P2P-звонки между вашими VU

Композитный шаг сводит пары VU через эфемерные shared-переменные — с driver: redis тот же тест охватывает несколько инстансов агента:

steps:
  - name: p2p call
    use: pro/webrtc-call@v1
    with:
      pairing:
        driver: redis
        key: webrtc-room-1
        strategy: adjacent       # vu 2k-1 звонит vu 2k
      media: bidirectional
      hold: 30s

Каждая ошибка несёт тег стадии — ice, dtls, signaling, publish, subscribe, hold — со счётчиками webrtc_call_errors_<stage>, так что регрессия сама указывает на фазу вместо общего «звонок упал».

Когда сеть сопротивляется

Соединения могут переживать ICE-disconnect вместо смерти: on_disconnect: restart выполняет настоящий ICE-рестарт — свежие креды пересигнализируются через WHIP PATCH (application/trickle-ice-sdpfrag) или через вашу библиотеку — до 3 попыток, с учётом в webrtc_ice_restarts_total. На приёме jitter_buffer_ms применяет настоящую задержку проигрывания по расписанию RTP-таймстампов — поглощение джиттера стоит TTFF того же размера, ровно как в проде. А sink: record сохраняет каждый принятый трек по каждому VU (.ogg / .ivf / .h264), чтобы можно было глазами увидеть, что сеть сделала с медиа.

Что измеряется

Сетап попадает рядом с вашими HTTP-перцентилями — webrtc_ice_duration_ms, webrtc_dtls_duration_ms, webrtc_setup_ms, webrtc_ttff_ms — плюс фоновый сэмплер getStats для качества медиа: RTT, джиттер, потери пакетов, битрейт по типам треков и per-track/per-layer серии для мультитрека. Гейтьте CI как обычно:

webrtc_setup_ms_failed: ["rate<0.05"]
webrtc_call_errors_ice: ["count==0"]

Где читать дальше

Полный справочник — все параметры, таблица кодеков и контейнеров, модель ошибок — в документации:

→ Документация WebRTC → Гайд по WebRTC в CLI

Если вы на Scale или Enterprise — направьте это на свой SFU и запускайте. Если ещё нет — это повод для апгрейда.

Комментарии

Ответить в Bluesky