iOS / WebView で TURN が失敗する?認証と “relay を使えているか” を先に確認
投稿日: 2026-02-18
webrtc
turn
coturn
authentication
ios
webview
troubleshooting
デスクトップでは動くのに iOS(またはアプリ内 WebView)で失敗する場合、まず (1) relay candidate が本当に出ているか、(2) TURN の username/credential がサーバ設定と一致しているかを確認してください。
チェックリスト + 擬似コードを見る
チェックリスト
- 一度 relay を強制: 一時的に
iceTransportPolicy: 'relay' で TURN 経路を確認。
- relay candidate を確認: candidate 文字列をログし、
typ relay を探す。
- 認証方式を混ぜない: static user/pass と期限付き(TURN REST)を混在させない。
- 時刻ズレ: 期限付き credential は時計ずれで失敗しやすい。
- WebView だけ失敗する場合: 診断として TURN over TCP/TLS も試す。
擬似コード:relay 強制 + candidate type のログ(標準 WebRTC API)
// PSEUDO-CODE (not WLSDK)
const pc = new RTCPeerConnection({
iceServers: [
{
urls: 'turn:turn.example.com:3478',
username: '<USERNAME>',
credential: '<CREDENTIAL>',
},
],
iceTransportPolicy: 'relay',
});
pc.onicecandidate = (e) => {
if (!e.candidate) return;
const c = e.candidate.candidate;
if (c.includes(' typ relay ')) console.log('relay', c);
};
プロダクトにライブ配信を埋め込むなら、まずは 統合ガイド(PHP SDK) の最小構成から。開発者アクセスが必要なら SDK を申請。
クラウド/PaaS で srflx が出ない?ローカルでは STUN が効くのに本番で消える理由
投稿日: 2026-02-18
webrtc
ice
stun
srflx
cloud
paas
deployment
troubleshooting
ローカルでは srflx が出るのに本番(cloud/PaaS)だと出ない場合、原因は egress 制限 やネットワークポリシーのことが多いです。まずは candidate の種類をログして状況を固定しましょう。
チェックリスト + 擬似コードを見る
チェックリスト
- candidate を全部ログ:
host だけか、srflx/relay も出ているか確認。
- UDP egress: STUN が通らない環境でもエラーにならずに “出ない” ことがあります。
- TURN をコントロールに:
relay は出るが srflx が出ないなら STUN 経路が怪しい。
- 最小 repro: 本番と同じ環境で candidate 収集だけをする最小ページを用意。
擬似コード:ICE candidate の種類をログ(標準 WebRTC API)
// PSEUDO-CODE (not WLSDK)
pc.onicecandidate = (e) => {
if (!e.candidate) return;
const c = e.candidate.candidate;
if (c.includes(' typ host ')) console.log('host', c);
if (c.includes(' typ srflx ')) console.log('srflx', c);
if (c.includes(' typ relay ')) console.log('relay', c);
};
埋め込み配信の baseline を整えるなら、まずは 統合ガイド(PHP SDK) の最小構成から。開発者アクセスが必要なら SDK を申請。
DTLS エラー:no SRTP profile negotiated — 勘で触る前に offer/answer を保存
投稿日: 2026-02-18
webrtc
dtls
srtp
sdp
whip
ffmpeg
interoperability
troubleshooting
no SRTP profile negotiated が出たら、いきなりパラメータを弄る前に、まずは SDP offer/answer を保存して、ブラウザが期待する DTLS-SRTP が成立しているかを確認するのが近道です。
チェックリスト + 擬似コードを見る
チェックリスト
- SDP を両方保存: 失敗ケースの offer/answer を丸ごと保存。
- fingerprint を確認:
a=fingerprint と a=setup の整合性を見る。
- SDP 書き換えに注意: signaling 層や proxy が必要な行を落とすことがあります。
- 相互運用: WHIP/FFmpeg/SFU 等を噛ませる場合、DTLS-SRTP(SDES ではない)を再確認。
- 変数を減らす: まずは audio だけなど m-line を最小にして再現。
擬似コード:offer/answer SDP をダンプ(標準 WebRTC API)
// PSEUDO-CODE (not WLSDK)
function dump(label, desc) {
console.log(label, desc.type);
console.log(desc.sdp);
}
const offer = await pc.createOffer();
await pc.setLocalDescription(offer);
dump('local', pc.localDescription);
// signaling 経由で `answer` を受け取った後
await pc.setRemoteDescription(answer);
dump('remote', pc.remoteDescription);
統合の baseline を保つなら、まずは 統合ガイド(PHP SDK)。開発者アクセスが必要なら SDK を申請。
モバイルで「音が出ない」:出力ルーティング、setSinkId(ある場合)と WebView の罠
投稿日: 2026-02-18
webrtc
audio
routing
devices
webview
ios
android
troubleshooting
モバイルの「音が出ない」は、録音ではなく 出力ルーティング や autoplay 制約が原因のことがあります。出力デバイスを列挙し、setSinkId は存在するときだけ使い、ブラウザと WebView を切り分けてください。
チェックリスト + 擬似コードを見る
チェックリスト
- ユーザー操作の後に再生: click 後に再生、
muted と音量も確認。
- デバイス列挙:
enumerateDevices() で audiooutput を確認。
setSinkId の有無: 環境によって未実装のため、存在チェック必須。
- devicechange: Bluetooth/有線の切替をハンドリング。
- 最小再現: remote track +
<audio> だけで確認。
擬似コード:出力デバイス列挙 + setSinkId(対応時)
// PSEUDO-CODE (not WLSDK)
async function listAudioOutputs() {
const devices = await navigator.mediaDevices.enumerateDevices();
return devices.filter((d) => d.kind === 'audiooutput');
}
async function setOutput(audioEl, deviceId) {
if (typeof audioEl.setSinkId !== 'function') {
console.warn('setSinkId is not supported');
return;
}
await audioEl.setSinkId(deviceId);
}
プロダクトにライブ配信を埋め込むなら、まずは 統合ガイド(PHP SDK) の最小構成から。開発者アクセスが必要なら SDK を申請。
接続しているのに再接続まで相手映像が出ない?Renegotiation(m-line順)と Perfect Negotiation を疑う
投稿日: 2026-02-04webrtcnegotiationrenegotiationsdptransceiverm-lineperfect-negotiationtroubleshooting
peer が “connected” でも、相手メディアが再接続後にしか出ないなら、まず疑うべきは renegotiation です。offer collision(glare)や、SDP の m=(m-line)順序が崩れるケース(特に transceiver 利用時)が原因になることがあります。
チェックリスト + 擬似コードを見る
チェックリスト
- 状態をログ: renegotiation 周辺の
signalingState/connectionState/iceConnectionState を保存。 - SDP の m-line 順を diff: 成功/失敗それぞれの SDP を保存し、
m= セクション順序を比較。 - transceiver を安定化: audio/video transceiver は固定順で一度だけ作成し、
replaceTrack() を優先。 - offer を直列化: 複数の
negotiationneeded で offer が同時進行しないよう gate を置く。 - Perfect Negotiation: polite/impolite + ignoreOffer で offer collision を一貫処理。
擬似コード:Perfect Negotiation(標準 WebRTC API)
let makingOffer = false;
let ignoreOffer = false;
const polite = true;
pc.onnegotiationneeded = async () => {
try {
makingOffer = true;
await pc.setLocalDescription();
sendSignal({ description: pc.localDescription });
} finally {
makingOffer = false;
}
};
onSignal(async ({ description, candidate }) => {
if (description) {
const offerCollision =
description.type === 'offer' &&
(makingOffer || pc.signalingState !== 'stable');
ignoreOffer = !polite && offerCollision;
if (ignoreOffer) return;
await pc.setRemoteDescription(description);
if (description.type === 'offer') {
await pc.setLocalDescription();
sendSignal({ description: pc.localDescription });
}
} else if (candidate) {
try {
await pc.addIceCandidate(candidate);
} catch (e) {
if (!ignoreOffer) throw e;
}
}
});
プロダクトにライブ配信を埋め込むなら、まずは 統合ガイド(PHP SDK) の最小構成から。開発者アクセスが必要なら SDK を申請。
DataChannel がバイナリ送信で固まる?bufferedAmount で backpressure を入れる
投稿日: 2026-02-04webrtcdatachannelbufferedamountbackpressurefile-transfertroubleshootingnodejselectron
小さいメッセージは送れるのに、バイナリ chunk(ファイル)で固まるなら、原因は backpressure 不足 の可能性があります。送信が速すぎると bufferedAmount が増え続け、UI やセッションが詰まります。
チェックリスト + 擬似コードを見る
チェックリスト
- 小さく始める: 16–64KB chunk から開始。
- 送信を gate:
dc.bufferedAmount と bufferedamountlow で制御。 - 状態確認:
dc.readyState === 'open' の時だけ送信。 - 受信側メモリ: 逐次処理し、巨大データを RAM に溜め込まない。
擬似コード:bufferedAmount backpressure(標準 WebRTC API)
const CHUNK = 64 * 1024;
async function sendFile(dc, buf) {
dc.bufferedAmountLowThreshold = 1024 * 1024;
for (let i = 0; i < buf.byteLength; i += CHUNK) {
while (dc.bufferedAmount > dc.bufferedAmountLowThreshold) {
await once(dc, 'bufferedamountlow');
}
dc.send(buf.slice(i, i + CHUNK));
}
}
function once(dc, evt) {
return new Promise((r) =>
dc.addEventListener(evt, () => r(), { once: true })
);
}
埋め込み配信の baseline を作るなら、まずは 統合ガイド(PHP SDK)。開発者アクセスが必要なら SDK を申請。
ブラウザ間で H.264 が動いたり動かなかったり?SDP の fmtp(profile-level-id)を確認
投稿日: 2026-02-02webrtch264sdpfmtpprofile-level-idinteroperabilitytroubleshooting
あるブラウザ/端末では ontrack が来るのに黒画面(または音声のみ)なら、ICE だけを疑わないでください。H.264 の場合、SDP の fmtp(特に profile-level-id)不一致が典型的な相互運用トラブルです。
チェックリストを見る
症状:track はあるがデコードできない
「Chrome では動くのに Safari/iOS で黒い」「PC は OK だが一部 Android で NG」などの形で現れます。接続自体は確立していても、ネゴシエーションされた H.264 プロファイルを decoder が拒否している可能性があります。
チェックリスト
- SDP を保存: うまくいくケース/失敗するケースそれぞれの offer と answer を保存。
- H.264 の payload type を特定:
a=rtpmap:<pt> H264/90000 を探して <pt> を控える。 fmtp を比較: a=fmtp:<pt> ... の profile-level-id / packetization-mode / level-asymmetry-allowed を比較。- プロファイルを合わせる: encoder を制御できるなら、プラットフォーム間で互換のある H.264 プロファイルに揃える(A/B として VP8 等に切替えて、原因が codec negotiation か確認するのも有効)。
- fmtp を壊さない: SDP を書き換える proxy/SFU/signaling 層が、H.264 パラメータを削除・改変していることがあります。
プロダクトにライブ配信を埋め込むなら、再現手順を最小化して観測可能にするのが近道です。まずは 統合ガイド(PHP SDK) の最小構成から始め、差分が見える状態になってから複雑さを足してください。開発者アクセスが必要なら SDK を申請。
Firefox で track は来るのに黒画面?mDNS ICE candidate(.local)を確認
投稿日: 2026-02-02webrtcfirefoxicemdnstroubleshooting
Chrome/Edge では動くのに Firefox だけ黒画面で、しかも track は存在する——まずは接続(ICE)を疑いましょう。特に候補に mDNS の .local が混ざる場合は要注意です。
チェックリストを見る
症状:track はあるがフレームが来ない
ontrack は発火するのに、video が黒いまま、ということがあります。これは RTP が実際には流れていない(またはネットワーク上で到達できない候補経路を選んでいる)ケースで起きます。
チェックリスト
- ICE 状態を確認:
iceConnectionState をログし、connected/completed になった瞬間の状態を保存。 - 選択された candidate pair を確認: Firefox では
about:webrtc で選ばれた local/remote candidate を確認。 - 候補に
.local があるか: これは mDNS host candidate の可能性があります。LAN/AP 構成によっては mDNS が解決できず「接続したのにメディアが来ない」状態になります。 - ホットスポットで A/B: ホットスポットは OK、社内 Wi‑Fi は NG なら、ほぼネットワーク経路 / 名前解決 / firewall の問題です。
- テストで relay-only: WebRTC 層を制御できるなら、一時的に relay-only(
iceTransportPolicy: 'relay')にして TURN relay 経路で症状が消えるか確認。
プロダクトにライブ配信を埋め込むなら、再現可能な baseline を維持するのが最優先です。まずは 統合ガイド(PHP SDK) の最小構成から始め、観測できるようになってから段階的に拡張してください。開発者アクセスが必要なら SDK を申請。
プレイヤーUIの言語を揃える:WLSDK の lang パラメータ
投稿日: 2026-01-31i18nlocalizationphpsdkiframe
サイトが多言語の場合、WLSDK::iframe()(または WLSDK::lazyIframe())呼び出し時に lang を渡すことで、プレイヤーUIの言語をページに合わせられます。
実装コードを読む
統合ガイドに明記されている内容
統合ガイドでは、プレイヤーが対応する言語コードとして en, ja, zh-Hant, zh-Hans, vi を列挙しています。lang を指定しない場合、WLSDK は実行環境の言語を読み取り、利用できない場合は en にフォールバックします。
例:プレイヤーUIを日本語に固定する
<?php
echo WLSDK::iframe([
'hostLabel' => 'connect',
'streamer' => '<STREAMER_USER_ID>',
'lang' => 'ja',
]);
?>
詳細は 統合ガイド → “Multi-language integration (optional)”。開発者アクセスが必要な場合は SDK を申請 してください。
マルチストリームダッシュボードの最適化:PHPによるライブビデオの遅延読み込み (Lazy Loading)
投稿日: 2026-01-31performancelazy-loadingphpscalingdashboard
ダッシュボードで多数のライブプレーヤーを同時に初期化すると、クライアントのCPUや帯域の負荷が増えることがあります。WLSDK::lazyIframe() を使って、必要になるまで初期化を遅らせましょう。
実装コードを読む
ダッシュボードは最初に読み込みすぎやすい
ページで多数のライブビューを一度に描画すると、ロード時点で複数のプレイヤーを同時に初期化してしまいがちです。大半が画面外なら、最初から全部を初期化する必要はありません。
統合ガイドの “Multiple Iframes Lazy Loading” パターンを使う
統合ガイドには WLSDK::lazyIframe() を使った複数 iframe の例があります。呼び出しは最小限にして、明確に記載されているパラメータだけを渡してください。
<!-- ループ内 -->
<?php foreach ($activeStreamers as $streamerId): ?>
<div class="stream-wrapper">
<?php
echo WLSDK::lazyIframe([
'hostLabel' => 'connect',
'streamer' => $streamerId,
]);
?>
</div>
<?php endforeach; ?>
詳細は 統合ガイド → “Multiple Iframes Lazy Loading”。
ブラウザの MediaRecorder が本番運用に向かない理由
公開日: 2025-12-28録画アーキテクチャ信頼性
MediaRecorder は手軽ですが、本番では シーク不可(メタデータ不足)、データ消失(RAMに依存)、可変フレームレート (VFR) による A/V ずれなどの落とし穴が出ます。
続きを読む
「簡単」に見えて後で壊れる
ブラウザ側だけで録画すると、運用で次が起きやすいです。
- シーク不可: WebM が duration/seek テーブル無しで出力されることがある。
- データ消失: タブクラッシュ/リロードで RAM 上のバッファが消える。
- VFR: 負荷でフレーム落ち → 音声と映像がずれる。
堅牢な解決策: サーバー側で録画/合成
サーバー側で合成・録画すれば CPU/ネットワークを制御しやすく、運用の再現性も上がります。SDK を検討している場合は、申請 の際に録画要件(保存先/フォーマット/保持期間など)を共有し、対応範囲を確認してください。
WebRTC でネットワーク切替(WiFi ↔ 4G)を扱う方法
公開日: 2025-12-28モバイル接続性ice-restart
WiFi から出ると IP が変わり、UDP の経路が途切れがちです。解は ICE Restart。pc.createOffer({ iceRestart: true }) で再収集して、ストリームを壊さずに復旧させます。
続きを読む
症状: 映像が固まる
ネットワークが切替わると IP が変わります。既存の UDP パスが無効になり、映像が止まることがあります。
対策: ICE Restart
iceConnectionState を監視し、disconnected / failed になったら ICE Restart を実行します。
pc.createOffer({ iceRestart: true })
これで 4G 側の新しい候補を集め直して、メディアトラックを維持したまま復旧できます。
埋め込み配信を運用するなら、再接続は自分の状態管理(ICE restart + UI)として設計するのが安全です。まずは 統合ガイド の最小構成をベースに、要件に合わせて拡張してください。
ICE Failed / TURN 接続トラブルシュート チェックリスト
公開日: 2025-12-25webrtciceturntroubleshooting
ICE Connection Failed は、だいたい firewall/NAT が原因です。TURN(UDP 3478)に加えて、企業ネットワーク向けに TCP 443(TURNS)フォールバックを用意しましょう。
続きを読む
症状とクイックチェック
ICE Connection Failed / candidate gathering 停滞は、ほぼネットワーク経路(firewall/NAT)か TURN 設定の問題です。
最低限の設定
- STUN/TURN: TURN を必ず用意(STUN のみは危険)。
- UDP 3478: 標準ポート。ただし企業網で UDP が塞がれていることも多い。
- TCP 443 フォールバック: UDP がダメでも通りやすい TURNS を用意する。
Symmetric NAT は P2P の敵
Symmetric NAT では直結が失敗しがちです。TURN 経由リレー を前提に設計しましょう。
TURN の運用が負担なら、マネージド TURN の利用を検討してください。選定時(または SDK を申請 する時)に、どのネットワーク要素が含まれるか/制限は何かを確認しておくと安心です。
どんなアプリにもライブ配信を埋め込む(クイックスタート)
公開日: 2025-12-25統合iframesdkjavascript
より安全に埋め込むなら、認証情報をブラウザに渡さないようバックエンドで <iframe> を生成します。統合ガイド の WLSDK::setup() + WLSDK::iframe() から始めましょう。
統合コードを見る
実用的な方法:バックエンドで embed を生成する
フロントエンドに player URL や認証情報を埋め込むのは避けましょう。バックエンドで embed HTML を生成して、ログイン済みユーザーに返す構成にすると、秘密情報をサーバー側に閉じ込めつつ、アクセス制御も一箇所で管理できます。
<?php
// WebLiveHub SDK class
require_once __DIR__ . '/vendor/autoload.php';
use WebLiveHub\SDK\WLSDK;
// SDK configuration
WLSDK::setup([
'hb_endpoint' => getenv('WL_HOSTED_BACKEND_URL') ?: '<HOSTED_BACKEND_ENDPOINT>',
'user_id' => '<AUTH_USER_ID>',
'password' => '<AUTH_PASSWORD>'
]);
?>
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8" />
<?php echo WLSDK::script(); ?>
</head>
<body>
<?php
echo WLSDK::iframe([
'hostLabel' => 'connect',
'streamer' => '<STREAMER_USER_ID>',
'attrs' => [ 'id' => 'stream-container', 'class' => 'wlh-stream', 'style' => 'border-radius:6px;' ]
]);
?>
</body>
</html>
SDK は <iframe> の embed を出力します。attrs で標準の HTML 属性を渡し、Autoplay/権限周りはターゲットブラウザ(特に iOS/Safari)で必ず動作確認してください。
SDK を使う理由
- バックエンド起点: 長期の認証情報をサーバーに置いたまま、ログインユーザーにだけ embed を返せます。
- 堅実な baseline: 統合ガイド(setup → script → iframe)を土台にして、必要な要件を追加できます。
👉 SDK を申請
WebRTC 統合の事前チェックリスト:環境を揃えてからデバッグする
公開: 2025-12-22webrtc統合チェックリスト互換性
「映らない/音が出ない」は環境要因が多いです。まず条件を揃えてからデバッグしましょう。
続きを読む
最初に baseline を揃えると、切り分けが速くなる
「映らない/音が出ない」は環境要因が多いです。まず条件を揃えてからデバッグしましょう。
ブラウザ/端末
- まず Chrome/Edge で検証(次に Safari/iOS)。
- カメラ/マイク権限とデバイスの存在を確認。
- シークレットで拡張の影響を除外。
配信/ネットワーク
- HTTPS 必須(ローカルは
localhost 可)。 - 社内ネットワークが UDP を制限することがあるため、モバイル回線で比較。
再生
- Autoplay 制限を前提に、クリックで開始。
- 出力デバイス/ミュート/音量を確認。
次:統合ガイド(PHP SDK) の最小構成で検証。必要なら SDK を申請。
WebRTC で音が出ない/映らない:再現可能な切り分け手順
公開: 2025-12-22webrtcトラブルシュート音声映像診断
闇雲に試すのではなく、状態を観測できる形にします。媒体、接続、再生の三段階で切り分けます。
続きを読む
媒体 → 接続 → 再生 の順で切り分ける
闇雲に試すのではなく、状態を観測できる形にします。
1) 媒体
- 権限は許可されているか?
- 別端末で再現するか(ハード/ドライバ要因の除外)。
2) 接続
- 社内ネットワークで不安定なら、モバイル回線で比較。
3) 再生
- クリック開始で Autoplay 制限を回避。
- 出力デバイスの取り違え(Bluetooth 等)を確認。
一度で十分な診断情報
- Chrome:
chrome://webrtc-internals の export/スクショ。 - 発生時刻、ブラウザ/OS、ネットワーク。
次:統合ガイド(PHP SDK) と照合して baseline を作ります。
Safari / iOS WebRTC の落とし穴:権限・バックグラウンド・Autoplay
公開: 2025-12-22webrtcsafariios互換性トラブルシュート
iOS は権限と foreground/background が厳しめです。テスト手順を固定化しましょう。
続きを読む
「Chrome はOK、iPhone はNG」が起きやすい理由
iOS は権限と foreground/background が厳しめです。テスト手順を固定化しましょう。
おすすめ手順
- 開始は必ずユーザークリックに紐付ける。
- バックグラウンド→復帰で回復できるか確認。
- 同条件で 3 回繰り返す(断続的な不具合検出)。
報告に必要な情報
- iPhone 機種、iOS/Safari バージョン
- Bluetooth 使用有無
- 低電力モード
次:統合ガイド(PHP SDK) の最小構成で iOS baseline を作ります。
配信品質と遅延の測り方:本当に見るべき指標
公開: 2025-12-22webrtc指標遅延品質可観測性
「カクつく」を数値に分解する。原因は loss/jitter/bitrate/端末負荷などの組み合わせです。
続きを読む
「カクつく」を数値に分解する
原因は loss/jitter/bitrate/端末負荷などの組み合わせです。
見るべき 5 指標
- Packet loss
- Jitter
- Bitrate
- Frames dropped / decode time
- End-to-end latency
取得方法
- Chrome:
chrome://webrtc-internals。 - 同条件で 3 回測って比較。
次:統合ガイド(PHP SDK) 完了後、品質 baseline を作ります。
SPA ライフサイクル実戦:マウント/アンマウント/再接続でリークを防ぐ
公開: 2025-12-22フロントエンドSPAライフサイクル安定性トラブルシュート
イベント/timer を片付けないと、二重接続やメモリ増加の原因になります。基本ルールを守りましょう。
続きを読む
SPA の落とし穴:画面は変わっても処理は残る
イベント/timer を片付けないと、二重接続やメモリ増加の原因になります。
基本ルール
- mount で登録
- unmount で解除(removeEventListener/clearInterval/リソース解放)
- 再接続は状態管理で多重実行を防ぐ
追加で見るイベント
次:統合ガイド(PHP SDK) と照合し、初期化とクリーンアップを対にします。