ウェブカメラ実ストリームテスト — 相手に実際に見えているもの
プレビューの映像はローカルのキャプチャビュー——getUserMedia() で届くカメラ映像——であり、センサーの生出力ではありません。相手が見るのは、WebRTCが黙ってネゴシエートし、圧縮し、格下げしうるブラウザのエンコード済みストリームです。本ガイドでは、コーデックネゴシエーションの仕組み、getStats() を使った実ストリームの測定方法、品質低下の一般的な原因の直し方を解説します。当サイトのウェブカメラツールはローカルキャプチャのみの確認であり、送信側のWebRTCストリームは測定しません。
ビデオ通話に座り、自分のウィンドウにはくっきりとした、よく照らされた1080pの映像が表示されています。見た目も整い、背景はきれいで、構図も申し分ない。それなのに同僚が「あなたの映像、ずっと止まってぼやけているよ」と言い、画面共有で見せてもらうと、そこにいるのは10年前の廉価ウェブカメラが作りそうな、ブロック状の、輪郭の甘い絵のような自分です。同じカメラ、同じ照明、同じマシン。それでいて、別の映像。
ほとんどのビデオ通話ガイドが飛ばしてしまう点を言います。このギャップは通常、ハードウェアの故障でもセンサーの欠陥でもありません——ただし、不具合のあるドライバーが原因になることはあります。典型的には、カメラが作り出すものと、ブラウザが実際に送るものとの違いです。プレビューはローカルキャプチャ経路——getUserMedia() を通じて届く、カメラが生成したフレーム——から供給されており、WebRTCによるエンコードの前の段階です。そのため、参加者が受け取る映像よりも好意的な見え方になります。とはいえ、そのビューにもブラウザが適用するスケーリング、色変換、制約処理が含まれうるため、センサーの生出力ではありません。参加者が受け取るのは、マシンを出ていく前のほんの数ミリ秒の間にWebRTCによってネゴシエートされ、再エンコードされ、レート制限され、場合によっては格下げされた、圧縮済みのストリームです。
そのエンコード層は、ユーザーインターフェースからは見えず、通常の動作中は沈黙しています。通話ウィンドウのどこにもその存在は告げられず、その決定は報告されず、プレビューはそれを反映しません。よくある場面を想像してください。通話の映像がぼやけるので、ユーザーは何時間もかけてカメラドライバーを再インストールする。そして chrome://webrtc-internals で発見するのです。ブラウザは帯域推定に基づいて480p15へネゴシエートダウンしていたのであって、原因はカメラでもドライバーでもなかった、と。このページでは、カメラセンサーと通話相手の画面の間で何が起きているのか、なぜブラウザは静かに品質を下げるのか、そして最も重要なこととして、プレビューを信用する代わりに、マシンを出ていく実際のストリームを測定する方法を解説します。

プレビューが見せていないもの
ビデオ通話は、あなたの意識の外だけで完結するパイプラインの上に成り立っています。あなたが送る各フレームは、次の段階を通過します。
- カメラセンサーがフレームを取り込みます。ブラウザはそのフレームを、要求された解像度とフレームレートで getUserMedia() 経由で届けます。ここにはスケーリング、色変換、制約処理が伴うことがあります。
- ブラウザはそのフレームを
getUserMedia()API を通じて受け取り、WebRTCエンジンへ渡します。ローカルプレビューはこの段階から供給されます。だからこそ、この先で何が起ころうとも、プレビューはいつだって完璧に見えるのです。 - WebRTCエンコーダーが、ネゴシエートされたコーデック、解像度、フレームレート、ビットレートで、フレームをビットストリームへ圧縮します。
- エンコード済みフレームはRTPパケットに分割されてネットワークへ送られ、通常はメディアサーバーを経由して全員へ中継されます。
- 各受信者はパケットをデコードして映像へ戻し、自分の画面に描画します。 プレビューは、パイプラインの第2段階にタップを差し込んでいます。表示されるのは、getUserMedia() を通じて届くカメラ生成のフレームであり、WebRTCエンコードの前のものです。ブラウザはこの段階でスケーリング、色変換、制約処理を適用することがありますが、WebRTCの圧縮は一切かかりません。通話相手が見るストリームは、第3段階から出てくるものです。この2点の間で、エンコーダーが世界に何を届けるかを決め、しかもあなたの見たことのない条件に基づいて決めているのです。 エンコーダーは3つのパラメータを黙ってネゴシエートし、そのどれもが、通知なしに通話の途中で変わりえます。
- 解像度 —— エンコードされるフレームの幅と高さ。1920×1080から1280×720、さらには640×480へ、勝手に落ちることがあります。
- フレームレート —— エンコードを生き残る毎秒フレーム数。解像度を変えずにデータレートを半減できるため、通常これが最初に落ちます(30から15、それ以下へ)。ただし、コーデックとプラットフォームによっては、解像度を先に下げる実装もあります。
- ビットレート —— エンコーダーが1秒あたりに使ってよいビット数。圧縮全体の度合いを決めます。 この3つの値が、相手に見えるすべてを決めます。低いビットレートに圧縮された1080pの映像は柔らかく滲んで見えます。突然10fpsで走り出した30fpsのストリームはガクガクと不自然です。プレビューがこれらの値をどれひとつ報告しないと理解した瞬間、「優秀なカメラなのにひどい通話」という謎は、溶けるように消えます。
最初のフレームを送る前にSDPがネゴシエートする
ネゴシエーションは、最初のフレームが送られる前に始まります。通話に参加すると、プラットフォームとあなたのブラウザは**セッション記述プロトコル(SDP)**のオファーとアンサーを交換します。SDPは各側がサポートするコーデックとメディア能力を広告します。実際の解像度とフレームレートはその後、送受信パラメータを通じてさらに交渉され、帯域推定によって制約されます。Windows 10 上の Chrome 125(2024年5月)での簡易テストでは、SDPアンサーはVP9、次いでH.264、次いでVP8の順で提示されることが観察されました。同時期の macOS 14 上の Safari 17 では H.264 が最初に現れました。これらの観察はブラウザごとに単一マシンで、管理されたネットワーク条件下で行われたものではないため、普遍的な順序を定義するものではなく、存在するばらつきを示す例です。実際に選択されるコーデックは、ブラウザ、プラットフォーム、SDPパラメータ、ハードウェア能力に依存します。一般的な選択肢にはVP9、H.264、VP8、AV1がありますが、全プラットフォームに共通する固定の優先順位はありません。 コーデック選択はまだ始まりにすぎません。通話がライブになると、2つ目の仕組みが引き継ぎます。帯域推定です。WebRTCはネットワーク経路を双方向で継続的に監視し、輻輳制御アルゴリズム——最も一般的なのは Google 輻輳制御——を回して、次の3つの測定をローリング方式で行います。
- パケットロス。受信側がRTCPフィードバックメッセージで報告します。
- 往復時間。パケット送信から、到着確認応答を受信するまでの遅延。
- スループット。データが実際に接続を横切る有効レート。
アルゴリズムはこれらの測定を、ネットワークが現在運べる帯域を推定するモデルへ投入します。その推定値が、エンコーダーへ渡されるターゲットビットレートとなり、エンコーダーは出力をそれに合わせて適応させます。ループは連続的で高速です。推定が落ちれば、エンコーダーは1秒未満のうちに低いビットレートで再エンコードします。
しきい値は近似値であり、使用している具体的な輻輳制御アルゴリズムに依存します。おおよそ5%を超える持続的なパケットロスは、ターゲットビットレートの引き下げを誘発しえますが、正確な挙動——どれだけレートが落ちるか、解像度とフレームレートのどちらも変わるのか、どのロス水準で変わるのか——は、アルゴリズム、コーデック、プラットフォームによって異なります。ロス率から特定の出力解像度への普遍的な対応表は存在しません。これらの決定を駆動するフィードバックループは(Google: WebRTC Congestion Control Whitepaper, 2019、RFC 8888: Congestion Control Feedback for RTP)に定義されています。30fpsの1080pストリームがきれいに見えるには、通常、毎秒約4〜6メガビットのアップロード帯域が必要です。ただし、コーデック、エンコード設定、シーンの複雑さに依存します。推定器が1メガビットしか利用できないと結論すれば、ストリームを無理に押し通そうとはせず、ターゲットを引き下げ、エンコーダーは解像度を480pへ、フレームレートを15fpsへ下げて応答します。推定がさらに落ちれば、映像は再び劣化し、ついには320×240の顔だけの映像まで下がります。すべてのステップが意図的なものです。目的は通話を生かし続け、オーディオを安定させることであり、映像品質はそのために最初に犠牲にされるのです。

ストリームが隠す3つの故障パターン
プレビューは、繰り返し現れる3つの故障パターンの背後に、ストリームの本当の状態を隠します。症状ごとの見分け方を覚えてください。正しい修正はケースごとに異なります。3つすべてに共通するサインがひとつあります。プレビューはエンコーダーの手前で標本採取されるため、終始完璧なままなのです。ダメージを見せるのは、エンコード済みストリーム——すなわち参加者の画面——だけです。
ビットレートの罠
症状: プレビューは完璧で、マシンのCPUには余裕がある。それなのに相手側には、一瞬たりともシャープにならない、ぼやけた低解像度の映像が見える。ローカル録画は優秀なのに、通話だけが貧弱に見える。 診断: ブラウザは、ネットワークがフルストリームを運べないと推定し、ビットレートに上限を設け、エンコーダーに解像度の縮小を強いています。制約となっているのはあなたのカメラではなく、アップロード帯域またはメディアサーバーの中継容量です。スピードテストを実行し、測定したアップロード速度を、1080p30が必要とする4〜6メガビットと比べてください。ストリーミング、ダウンロード、同期を行う他のデバイスと回線を共有しているなら、彼らが同じ予算を奪い合っています。
ロスしきい値
症状: 通話の静止画は問題なく見えるのに、動きのある部分はガクガクとロボットのようになる。オーディオは滑らかなまま映像だけがもたつき、数秒ごとに短いフリーズが起きては、低いフレームレートで映像が再開する。 診断: パケットロスが断続的に5%のしきい値をまたいでおり、輻輳コントローラーが繰り返しターゲットビットレートを削り、エンコーダーがそれに合わせてフレームを落としています。フレームレートが最初に落ちるのは、それが最も安価なレバーだからです。30fpsから15fpsへの削減は、解像度を変えずにデータレートを半減させます。Wi-Fi干渉、限界近くのEthernetケーブル、ロスとレイテンシを上乗せするVPNを確認してください。ネットワーク経路が修復されるまで、映像は劣化し続けます。
キーフレームフリーズ
症状: 参加直後、または画面共有を開始した直後の最初の2〜5秒、相手には凍りついた、あるいはひどく滲んだ映像が見える。その後、映像は一気にシャープになって正常に動き、次の大きなシーンチェンジまでそのまま続く。
診断: 映像コーデックは2種類のフレームを送ります。キーフレームは映像全体をエンコードし、固定間隔——プラットフォームと構成に依存しますが、一般に1〜10秒ごと——で現れます。デルタフレームは前フレームからの変化だけをエンコードするため、ごく小さくなります。デルタフレームのパケットが失われると、受信側はそのフレームを再構築できません。再送を要求する(NACK)か、送信者に新しいキーフレームを求める(PLIまたはFIR)か、待つ間その誤りを隠蔽するかです。要求が成功すれば回復は速く、失敗すれば、次に予定されたキーフレームが到着するまで映像は凍ったままです。あなたが目にするフリーズは、その待ち時間です。キーフレーム間隔が短ければ回復は速くなりますが帯域を消費し、間隔が長ければ効率的である代わりに、あらゆるロスが目立ちます。これは故障ではありません。設計どおりに動いているコーデックなのです。

getStats() で実際のストリームを測る
当て推量はやめましょう。数字は気持ちよくないかもしれませんが、真実です。WebRTC API は、エンコーダーが何をしているかを正確に報告する統計インターフェースを公開しており、自分で管理する任意のWebページは自身のピア接続を読めます。RTCPeerConnection オブジェクトに対して getStats() を呼び、返されたレポートから、kind が video である outbound-rtp エントリーを探してください。media-source と outbound-rtp のエントリーに、重要な値があります。
- framesPerSecond(media-source エントリー上)—— カメラがエンコーダーへ届けているフレームレート。30fpsカメラでこの値が30を下回るなら、キャプチャ経路自体に問題があります——多くの場合はUSBバス競合の問題であり、USBバス競合ガイドが診断方法を説明しています。
- framesPerSecond(outbound-rtp エントリー上)—— エンコーダーが実際に生産しているフレームレート。これが入力レートを下回っているとき、エンコーダーはフレームを落としています。
- frameWidth と frameHeight(outbound-rtp エントリー上)—— エンコード解像度。通話設定と比較してください。違いがあれば、それは黙って行われた格下げです。
- bytesSent(outbound-rtp エントリー上)—— エンコード済みデータの累計。1秒間隔で2回標本を採り、差に8を掛ければ、実際のビットレートが分かります。
- packetsSent と packetsLost —— outbound-rtp エントリー上の packetsLost は常に埋まるとは限らない点に注意してください。ロス率は、受信側の視点を報告する remote-inbound-rtp エントリーから読むほうが確実です。
- currentRoundTripTime(candidate-pair エントリー上)—— ネットワーク遅延。高いRTTとロスの組み合わせは、輻輳を示します。 ブラウザごとにフィールド名が微妙に異なったり、一部の統計が丸ごと省略されたりすることがあるため、可能な場合はブラウザの WebRTC internals ページと突き合わせてください。 解釈の規則は単純です。outbound-rtp の framesPerSecond が、media-source の framesPerSecond を目に見えて下回っているなら、エンコーダーは圧力の下でフレームを削っています。frameWidth と frameHeight が、設定した解像度を下回っているなら、接続の予算はそれを運べなかったということです。bytesSent が、その解像度についてコーデックの要求を大幅に下回るビットレートに換算されるなら、映像は潰されています。そして、エンコーダーを決して見ないプレビューは、終始正常に見えるのです。 これらの値を読むために、コードを書く必要はありません。Chrome と Edge は chrome://webrtc-internals に内蔵の統計ビューアーを搭載しており、ブラウザ内のすべてのWebRTC接続——通話プラットフォームが作ったものを含みます——を捕捉します。会議に参加する前に開き、記録させたままにしておいて、後からビデオ送信者の outbound-rtp エントリーを調べてください。同じフレームレート、解像度、ビットレート、ロスの数値がそこにあり、エンコーダーがいつ出力を変えたかを正確に示すタイムライングラフも添わっています。実際のプラットフォーム通話で格下げを現行犯で捉える最速の方法です。 自分の通話がバックグラウンドでアイドルの状態でこの測定を行い、次に忙しい会議中にもう一度行い、2組の数値を比べてください。典型的な比較:アイドル時、outbound-rtp の framesPerSecond は30を読みます。忙しい会議中は18まで落ちることがあり、その間もローカルプレビューは30を表示し続けます。この比較は、ベースラインのネットワークが十分かどうか、そして格下げの引き金になっているのが通話中の競合かどうかを明らかにします。当サイトのウェブカメラテストは、getUserMedia() でネゴシエートされたローカルキャプチャトラックの解像度とフレームレートを表示します。カメラがブラウザへ何を届けられるかを知るための有用なベースラインですが、ピア接続かブラウザのWebRTC internals ページを必要とする、送信側のWebRTCエンコード済みストリームの測定ではありません。
Zoom、Meet、Teams:3つの異なるカメラ
同じカメラ、同じノートPC、同じネットワークでも、アプリケーションごとに見た目が違う結果になりえます。どのプラットフォームも、ブラウザのWebRTC実装の上に独自のエンコードポリシーを適用するからです。
- Zoom は一般に安定性を優先します。バージョン、アカウント種別、会議設定に応じて、送信解像度に上限を設け、オーディオと画面共有を安定させ続けるために映像品質を犠牲にすることがあります。1080p対応のカメラでも、Zoom上では720p以下で送信されることがあります。
- Google Meet は測定された帯域へ適応する傾向があり、早めに解像度を落とし、状況が改善すれば素早く戻します。その挙動はあなたの実際のネットワーク状態を追いかけます。良い意味でも、悪い意味でも。
- Microsoft Teams は、独自のエンコードとポストプロセッシングのパイプラインを適用します。バージョンと会議設定に応じて、映像は素のWebRTCネゴシエーションを超えて、さらに圧縮されることがあります。
- ブラウザネイティブのWebRTC——素のWebページで得られるもの——は、その上にプラットフォームのポリシーを載せず、ネゴシエート済みのストリームを送ります。素のWebRTCにおいて、ハードウェアとネットワークが何を届けられるかという意味で、最も地面に近い真実です。プラットフォームのポリシーはバージョン、アカウント種別、会議構成によって変わるため、これらは固定の規則ではなく、一般的な傾向として扱ってください。 実用上の帰結はこうです。Zoom通話とMeet通話を比べることは、カメラテストではありません。それは各プラットフォームのエンコード判断のテストです。まず、素のWebRTCページかウェブカメラテストツールで、生のストリームを測定してください。生のストリームがきれいで、それでも特定のプラットフォームだけが見栄え悪いなら、制約はプラットフォームのポリシーであり、ハードウェアのアップグレードでは変わりません。プラットフォーム通話の進行中は、chrome://webrtc-internals でそのエンコーダーの判断をリアルタイムで観察できるため、格下げがプラットフォームのポリシーによるものかネットワークの挙動によるものかを、どちら側の自己申告も信用せずに確認できます。
クリーンなストリームへの7つのステップ
これらのステップは、個々の原因を切り分けます。ストリームが正しく見えた時点で止めて構いません。ただし、修正が本当に数字を動かしたことを確かめるため、各変更の前後に測定してください。
- 最初に測定する。 chrome://webrtc-internals を開くか、自分のピア接続で getStats() を使い、解像度、フレームレート、ビットレートを記録します。生のストリームがすでに格下げされているなら、問題はカメラと接続にあります。きれいなら、問題はプラットフォームにあります。
- 帯域を確認する。 ビデオ通話には、720pで最低1Mbpsのアップロード、1080pでおよそ4Mbpsが必要です。別のデバイスからではなく、通話に使うマシンからテストし、ネットワーク上の他のトラフィックが乗った状態で再テストしてください。可能なら有線接続へ移行します。Wi-Fiのロスは、見えない格下げの最大の一般的原因です。
- 照明を直す。 よく照らされた被写体は、暗い被写体より劇的に圧縮しやすくなります。エンコーダーがノイズではなく、可視のディテールにビットレートを費やせるからです。窓の方を向くか、ソフトライトを使います。費用はゼロであり、しばしばどの設定よりも大きな改善をもたらします。
- 環境がすでに静かで十分に照らされているなら、映像ノイズリダクションを無効にする。 一部のプラットフォームは低照度シーンで積極的なデノイジングを適用し、エンコード済みストリームの細部を目に見えて柔らかくすることがあります。ローカルプレビューにはこの違いは現れません。エンコード済みストリームだけがそれを示します。どのコントロールが利用できるかは、各プラットフォームの設定で確認してください。
- バーチャル背景を無効にする。 背景のセグメンテーションは、実効ビットレートにオーバーヘッドを加え、エンコーダーからGPUサイクルを奪います。一貫した照明の下のすっきりした実背景は、どんなバーチャル背景にも勝ります。
- コーデックを確認する。 CPUが耐えられるなら、VP9がビットあたりの品質で最良です。フレームドロップやCPUスパイクが見えるなら、H.264のほうが速くエンコードできます。設定でコーデックの優先を指定できるプラットフォームもあります。
- 別のプラットフォームで試す。 あるサービスでは品質が良く、別のサービスでは悪いなら、制約はプラットフォームのエンコードポリシーです。期待値か、会議ツールのどちらかを調整してください。ハードウェアには落ち度がありません。
まとめ
プレビューは、あなたのカメラのローカルな自画像です。カメラがブラウザへ届けられるものの、好意的な見え方です。ストリームは、世界が実際に見ているもの——ネットワークの状況とプラットフォームのポリシーに形作られ、ネゴシエートされ、圧縮されたものです。両者は同一被写体の2つの別個の動画であり、あなたがどう印象を与えるかに関係するのは、その片方だけです。「自分の画面ではカメラの映像が素晴らしいのに、通話ではひどい」とその理由を不思議に思ったことがあるなら、あなたは今、答えと、それを測るための道具を手に入れました。
chrome://webrtc-internals を開いてください。次の通話に参加し、outbound-rtp エントリーを見張ってください。frameWidth か framesPerSecond が、カメラ設定で決めた値を下回っていたら、格下げは発生しています。原因は典型的には帯域関連——ブラウザに許された支出額と、あなたの接続が、送っているつもりのストリームを実際に運べるかどうか——です。しかし、CPU負荷、エンコーダー選択、プラットフォームのポリシー、制約の構成もまた、劣化に寄与したり、劣化を支配したりしえます。