HTMLCanvasElement.captureStreamで取得した配信の画質を極力落とさずに配信する

# はじめに
この記事は「[リアズ Advent Calendar 2022](https://adventar.org/calendars/7761)」の18日目の記事です。

皆さんご無沙汰しております。リアズフロントエンジニアのよしだです。
早いもので今年も残りわずかとなりました。
流行病の影響で今年は相変わらずステイホームすることが多かったのですが、
皆さんはどうでしたでしょうか？
(といっても割とインドアなのでそもそも外に出歩かない…)

毎年アドベントカレンダーのネタは今年実装した中で印象に残っていることにしてました。
今年はつい最近まで自分が進めていたとある案件の問題点と解決法についてお話ししたいと思います。

# 目次
1. [弊社の運営するサービス『萌えチャット』について](#Chapter1)
1. [リリース直前になったSP版の画質が悲しいくらい荒い](#Chapter2)
1. [対応方法](#Chapter3)
1. [終わりに](#Chapter4)

<!-- 各チャプター -->
<a id="#Chapter1"></a>
# 弊社の運営するサービス萌えチャットについて
弊社のサービスの中に[萌えチャット](https://www.angel-live.com/moe/)というものがあります。
音声とキャラクターの描画を用いてライブチャットを行うサービスです。これにより顔出しはちょっと…だけど声には自信のある女性の方もライブチャットを行うことができます。今で言うとVTuberが近いでしょうか。
こちらのサービスは元々flashで動いてたのですが2020年末でflashのサービスが終わり、それに伴いhtml5での配信に切り替わりました。ちなみにリアズに来て最初の大きな案件はこの萌えチャットのhtml5化でした。

しかし、当時は時間や[HTMLMediaElement.captureStream](https://developer.mozilla.org/ja/docs/Web/API/HTMLMediaElement/captureStream)がSafari未対応と言うこともあり、スマートフォンの対応は見送りとなりました。
Safariはこの件以外にも独自の仕様があり、フロントエンド開発泣かせのブラウザだったりします🥺
とはいえ今年の１月のアンケートではやはりPCよりスマートフォンでの配信をさせてほしいという要望が多く、なんとか用意する必要がありました。
そこでvideoタグの映像をcanvasに転写し、そのcanvasに再生されている映像をwebRTCで配信する方法[HTMLCanvasElement.captureStream](https://developer.mozilla.org/ja/docs/Web/API/HTMLCanvasElement/captureStream)で対応することにしました。
仕組みとしては下記のような感じです

```
// 元動画
const localVideo = document.getElementById('video');
// 転写用canvas
const localCanvas = document.getElementById('canvas');

dispatch(※音声だけを取得する処理).then((audioUserMedia) => {
mediaStream = audioUserMedia;
// 30フレームごとにvideoタグで再生されている映像をcanvasに転写する処理を行う
window.setInterval(function () {
      const localCanvas = document.getElementById('canvas');
      localCanvas.getContext('2d').drawImage(videoタグ, 0, 0, canvasの幅, canvasの高さ);
}, 30);
// canvasの描画開始
localCanvas.getContext('2d').drawImage(videoタグ, 0, 0, canvasの幅, canvasの高さ);
//送信用ストリーム作成
let canvasStream = localCanvas.captureStream();
//　canvasに描画されている映像にマイクからの音声を合わせる
canvasStream.addTrack(mediaStream.getAudioTracks()[0])
resolve(canvasStream);
```



音声が送れないなどのトラブルはありましたが、今年の秋ようやく配信できる状態になり、そして先々週に無事開発が終わりました。ここで終わってくれればよかったのですがね。。。

<a id="#Chapter2"></a>
# リリース直前になったSP版の画質が悲しいくらい荒い

そろそろリリースの案内をしないといけない１２月の初旬、筆者はある問題に悩まされていました。
![スクリーンショット 2022-12-16 16.33.47.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/946964/585f8268-fbfd-46c8-2b16-d83422085458.png)

そう、スマートフォンで配信するとなんか**むっちゃぼやける**
会社のつよつよ回線でこれだったので自宅の回線やwifiで配信するとブロックノイズまで出てしまいました。
いくらパフォーマーの方が頑張っても全体にモザイクかかったような状態では~~抜けない~~
十分なパフォーマンスはできないでしょう。

当初はweb通信の問題かと色々調査していましたが原因は至ってシンプル、
**配信映像を映すcanvasの解像度が小さい**からでした。
実際のスマートフォンでの配信画面が下記のようになるのですが、見ていただくとわかるように結構配置的に余裕がなくギリギリなことがわかります。スクリーンショットなので大きく見えますが実機だとより小さく見えることでしょう。
![FkE5LKAUEAE6X9B.jpeg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/946964/4345d44e-e7ad-1a98-5b26-0fd8a6f52232.jpeg)

改修前の映像の解像度は下記の状態です。
![資料1.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/946964/952478ba-02e5-20e7-77fb-9584e774bcbd.png)
パフォーマーが配信時に映像を確認する際、スマートフォン画面の都合上小さい解像度の動画を見ることになります。
従来はそこに描画されている映像をそのまま配信していたため、会員側では映像が引き伸ばされモザイクのようになっていた訳でした。パフォーマー側の映像は解像度が小さくても綺麗に表示されていたので問題ないと当初は思ってましたが、よくよく考えればサーバーから取得したmp4ファイルを再生しているのでサイズが小さくても綺麗な映像なのは担保されていることは当たり前でした。。
<a id="#Chapter3"></a>
# 対応方法

原因が解像度ならシンプルにcanvasの大きさをより大きくすれば問題ないのではと思い、早速取り掛かりました。
流石にこれ以上スマートフォン上に要素を用意できないので、思い切って画面外にクソデカvideoタグとcanvasを用意しました。
配信画面はスクロールする必要がないので要素の大元となる要素にスクロールを防止するcss『overflow: hidden;』をあて、パフォーマー側はその存在を意識させないように実装しました。
そしてcanvasは２つ用意しました。
一つは『パフォーマーが映像を確認するcanvas』、
もう一つは『webRTC配信用の映像を描画するcanvas』です。
図にすると下記のようになります。
![資料2.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/946964/daf80426-1811-d0ea-ebac-62e2c536833d.png)

これにより画質が大幅に向上し、実際の会員側で見る映像も画質の劣化が起きることなく配信することができました。
![スクリーンショット 2022-12-16 16.38.46.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/946964/969a2da6-b50c-5a9b-84c2-342d88bc1964.png)

この問題から**映像の要素の大きさ(解像度)次第でwebRTCの映像の劣化**を防ぐことができるかもしれないと言うことを学べました。
当たり前といえば当たり前なのですが、通信状況や端末を疑う前に、こういったシンプルな原因を追求することも大事なのだなと今回の実装で痛感しました。

<a id="#Chapter3"></a>
# 終わりに
なんとか今年中に開発が完了し一安心。。といったところですが、まだまだやるべき案件は多く残っており、萌えチャットのサービスについてもまだまだ課題があります。
来年はエンジニアとしてもさらに知見を増やしていき、こういった不測の事態に対しての引き出しを多く持ちたいですね。
そういや今年のアドベントカレンダーは僕が最後みたいです。来年こそはアドベントカレンダー以外でもこういった技術者的な発信をできたらいいですね。
できるかな…？ぼちぼち頑張ります。では良いクリスマスを ノシ