WebRTCオッサン問題を超えて。ローカルファイルを自在に操りたい

# はじめに

前回の記事[「WebRTCを試すときにオッサンが映り続ける問題に対処する」](http://qiita.com/massie_g/items/5a6c4b69374d5997dc37)は予想外の反響をいただきました。今回はその続編として、ローカルファイルからメディアストリームを作って色々操作してみました。

# その後に分かったこと

前回の記事を書いた後にコメントを頂いたり、自分で試したりして、分かったことがあります。

* CanvasやWeb Audioを使わなくても、videoタグやaudioタグから直接captureStream()できる
    * Firefox 47ではプレフィックス付きの mozCaptureStream()が使える
    * Chromeでも近い将来 captureStream()が使えるようになる
* 動画系のファイルから、音声だけ取り出して変換することができる
    * audioタグで動画ファイルの音声だけ再生可能
    * Web Audioで音声だけ変換可能
    * ※ わざわざ映像ファイルと音声ファイルを別個に用意する必要はない

前回の記事のようにまどろっこしい処理は不要になりました。嬉しいやら、悔しいやらです。

# あえてCanvasが必要なケースを作り出す

負け惜しみとして、あえてCanvasを使ってメディアストリームに変換するサンプルを作って見ました。2つの映像ファイルを用意して、自由に切り替えられるようにしたのがこちらです。

#### 2つの映像の切り替え
![demo_fade.gif](https://mganeko.github.io/webrtcexpjp/tool/demo/demo_fade.gif)

切り替えの様子の[動画はこちらから(m4v形式、 14MB)](https://mganeko.github.io/webrtcexpjp/tool/demo/switcher_demo.m4v)
※今回もこちらの[NHKクリエイティブライブラリ](http://www1.nhk.or.jp/creative/)の素材を使わせていただきました。

#### サンプルページ、ソースコード

Chrome 51, Firefox 47で動かすことができます。

* GitHub Pagesで試す [switch_to_stream.html](https://mganeko.github.io/webrtcexpjp/tool/switch_to_stream.html) 
* GitHubで見る [switch_to_stream.html] (https://github.com/mganeko/webrtcexpjp/blob/master/tool/switch_to_stream.html)

上のアニメーションのように操作する前に、まず映像ファイル×2、音声ファイル×1を指定して、[Start]してからお楽しみください。
![demo_start.gif](https://mganeko.github.io/webrtcexpjp/tool/demo/demo_start.gif)

# 仕組み

仕組みといっても、ファイルからメディアストリームに変換する方法は[前回の記事](http://qiita.com/massie_g/items/5a6c4b69374d5997dc37)に記載した内容と同じです。そこで、2つの映像をCanvasで合成する部分について説明します。

### スワイプの場合

* スライダーが0の時はvideo 1の内容を、100の時はvideo 2の内容をCanvasに転写しています
* あいだの場合は、その割合に応じてvideoの一部を切り出して、 drawImage()で転写します
    * 30ならvideo 1が70%、video 2が30%の比率にしています

<img width="698" alt="swipe30.png" src="https://qiita-image-store.s3.amazonaws.com/0/19403/8d76c445-4dad-d08a-bc4f-b8eacd4d55c7.png">

```js:swipe
      let video1Width = Math.round((100- faderValue)/100.0 * localVideo.videoWidth);
      let video2Width = Math.round(faderValue/100.0 * localVideo2.videoWidth);
      let canvasWidth1 = Math.round((100- faderValue)/100.0 * duplicateCanvas.width);
      let canvasWidth2 = Math.round(faderValue/100.0 * duplicateCanvas.width);

      if (canvasWidth1 > 0) {
        ctxDuplicate.drawImage(localVideo, 0, 0, video1Width, localVideo.videoHeight,
          0, 0, canvasWidth1 , duplicateCanvas.height
        );
      }

      if (canvasWidth2 > 0) {
        ctxDuplicate.drawImage(localVideo2, (localVideo2.videoWidth - video2Width), 0, video2Width, localVideo2.videoHeight,
          (duplicateCanvas.width - canvasWidth2), 0, canvasWidth2 , duplicateCanvas.height
        );
      }
```

### ストライプの場合

全体を10個に分割し、その1列ごとにスワイプと同じ処理を行っています。
<img width="730" alt="stripe30.png" src="https://qiita-image-store.s3.amazonaws.com/0/19403/6f2d9ee7-4fad-7728-561d-523eae727ebf.png">

### ディゾルブの場合

片方を透けさせながらもう片方に切り替えるディゾルブの処理はについては、globalAlphaという指定を使って実現しています。
<img width="717" alt="dissolve30.png" src="https://qiita-image-store.s3.amazonaws.com/0/19403/c941746c-70ef-7d21-08d3-dd50fec2cbfe.png">

```js:dissolve
      let video1Alpha = (100 - faderValue)/100.0;
      let video2Alpha = (faderValue)/100.0;
      
      ctxDuplicate.globalAlpha = video1Alpha;
      ctxDuplicate.drawImage(localVideo, 0, 0, localVideo.videoWidth, localVideo.videoHeight,
        0, 0, duplicateCanvas.width , duplicateCanvas.height
      );
      
      ctxDuplicate.globalAlpha = video2Alpha;
      ctxDuplicate.drawImage(localVideo2, 0, 0, localVideo2.videoWidth, localVideo2.videoHeight,
        0, 0, duplicateCanvas.width , duplicateCanvas.height
      );
```

変換したメディアストリームは、RTCPeerConnectionで通信相手に送ることができます。（※ new MediaStream() でオブジェクトを作っているため、現状ではFirefoxではエラーになってしまいます）

# おわりに
ローカルファイルをあえてCanvasを使って変換する例をご紹介しました。VJ気分で映像を切り替えながら配信するなんてことが、ブラウザだけでもできるようになりますね。（※既存の動画/音声ファイルを使う場合には、その利用条件は尊重してご利用ください。よろしくお願いします）

[HTMLExperts.jp](https://html5experts.jp)というサイトで[「WebRTC入門2016」](https://html5experts.jp/series/webrtc2016/)という連載も書いているので、WebRTCにご興味を持たれた方はそちらもご覧いただけると嬉しいです。