vue.jsで複数のvideoタグを扱う

vue.js+MediaStreamを用いてvideoタグの扱い、および複数のビデオタグを扱う方法について。

vue.jsでvideoタグを使った再生をする場合、src属性を指定する方法やjsからsrcObjectを指定する方法がよく書かれていますが、vue.jsの機能でバインディングする方法があまり書かれていないのでまとめ

# 結論

`:srcObject.prop`で`MediaStream`インスタンスを指定する

```vuejs

<template>
  <div>
    <video autoplay :srcObject.prop="stream"></video>
  </div>
</template>

<script>
export default {
  name: 'app',
  data(){
    return {
      stream: undefined
    }
  },
  async created() {
    this.stream = await navigator.mediaDevices.getUserMedia({
      video: true,
      audio: true
    })
  }
}
</script>
```

参考
[Binding to srcObject attribute](https://forum.vuejs.org/t/binding-to-srcobject-attribute/39323)

# src属性による指定

よく出てくる情報はsrc属性を指定する方法です。
単純にvueのバインディングを使えば良いのでシンプルです。

```vuejs
<template>
  <video src=":src" autoplay></video>
</template>

<script>
export default {
  data() {
    return {
      src: "url"
    };
  },
};
</script>
```

ただ、この方法は映像ファイルのURLが存在する必要があるため、ストリームを使用する場合は`URL.createObjectURL`を使って変換する必要があります。
ただ、このメソッドが廃止予定となっており、現在では`src`の代わりに`srcObject`を使うことが推奨されています

# srcObjectにバインドする方法

htmlの属性であるsrcと違い、srcObjectはプロパティなので、通常の`:srcObject="stream"`ではなく、`:srcObject.prop="stream"`と指定する必要があります。

> 参考
[Binding to srcObject attribute](https://forum.vuejs.org/t/binding-to-srcobject-attribute/39323)



あとは、この`stream`に 映像や音声のストリームデータを格納すればストリームのデータを再生してくれます。

```vuejs

<template>
  <div>
    <video autoplay :srcObject.prop="stream"></video>
  </div>
</template>

<script>
export default {
  name: 'app',
  data(){
    return {
      stream: undefined
    }
  },
  async created() {
    this.stream = await navigator.mediaDevices.getUserMedia({
      video: true,
      audio: true
    })
  }
}
</script>

```



# 複数のvideoタグを使う場合

もちろん`v-for`でvideoタグを複数扱うこともできます。
`add`ボタンを押すと自分の顔がいっぱい並ぶので注意してください

```vuejs
<template>
  <div>
    <div>
      <button @click="add">add</button>
    </div>
    <div>
      <video v-for="(stream, i) in streams" autoplay :key="i" :srcObject.prop="stream"></video>
    </div>
  </div>
</template>

<script>
  export default {
    name: 'app',
    data() {
      return {
        streams: []
      }
    },
    methods: {
      async add() {
        const stream = await navigator.mediaDevices.getUserMedia({
          audio: true,
          video: true
        });
        this.streams.push(stream);
      }
    },
  }
</script>
```

# 備考:自動再生したい場合

vue.jsなしのJavascriptで扱う場合はよく読み込み後に`play`メソッドを叩いて自動再生処理を行いました。こうしないとブラウザのポリシーによっては映像が再生されないので。
Chromeはバージョンによってこの辺のポリシーがコロコロ変わったので、割と振り回されました。

vue.jsのバインディングを利用する場合は以下２つの方法で可能です。

## autoplay属性を設定

簡単な方法であれば`autoplay`属性を指定する方法です。
この属性を指定しておけば読み込み後に自動再生してくれます。

```vuejs
<template>
    <video autoplay :srcObject.prop="stream"></video>
</template>

<script>
export default {
  data() {
    return {
      stream: undefined
    };
  },
  async created() {
      this.stream = await navigator.mediaDevices.getUserMedia({audio: true, video: true})
  }
};
</script>

```

## loadeddataイベントで再生

あるいは`loadeddata`イベントを拾って、playメソッドを叩く方法でも良いです。
再生以外の処理も挟めるので、以前と同じような処理をしたいと考えた場合はこちらの方が良いと思います。

```vuejs

<template>
  <div>
    <video @loadeddata="onloadeddata" :srcObject.prop="stream"></video>
  </div>
</template>

<script>
export default {
  name: 'app',
  data(){
    return {
      stream: undefined
    }
  },
  methods: {
    onloadeddata(ev) {
      ev.target.play();
    },
  },
  async created() {
    this.stream = await navigator.mediaDevices.getUserMedia({
      video: true,
      audio: true
    })
  }
}
</script>

```

# まとめ

vue.jsでvideoタグを扱う方法をまとめました。
WebRTC等を組み合わせればハングアウト等のようなビデオチャットツールが作れると思います。
どちらかというと通信量などのパフォーマンス周りでハマると思いますが。。。