皆さん、こんにちは。どんぶラッコです。
今日は、getDisplayMedia
を活用して画面画面共有の機能をWebで実装する方法についてまとめていきます。
Google Meet や Zoom で実装されていますよね。
実はあの機能、JavaScriptの標準WebAPIを使って実装することができてしまうんです。
イメージはこんな感じ↓↓
今回は Vue3 で実装したので、コンポーネントの情報を乗っけておきたいと思います。
コンポーネント実装例
<script setup type="typescript">
import ScreenCapture from './components/ScreenCapture.vue';
</script>
<template>
<v-app>
<v-main>
<v-container>
<ScreenCapture />
</v-container>
</v-main>
</v-app>
</template>
App.vueでやっていることは単純です。 ScreenCapture コンポーネントを表示させているだけ
では、ScreenCapture.vue
で何をしているかというと、こんな感じ。
<template>
<v-btn
color="primary"
class="font-weight-bold"
elevation="0"
@click="onClickSelectButton"
>
画面1
</v-btn>
<video
:srcObject.prop="videoSrc"
autoplay
width="400"
/>
</template>
<script lang="ts">
import {
ref,
reactive,
defineComponent,
} from 'vue';
import useMediaDevice from '../composables/useMediaDevice';
export default defineComponent({
setup() {
const videoSrc = ref<MediaStream>(new MediaStream());
const {getDisplayMedia} = useMediaDevice();
const onClickSelectButton = async () => {
const mediaStream = await getDisplayMedia();
if (!mediaStream.active) {
return false;
}
console.log('mode');
videoSrc.value = mediaStream;
};
return {
videoSrc,
onClickSelectButton,
};
},
});
</script>
<style lang="scss">
</style>
getDisplayMedia()
メソッドを composables ディレクトリに定義して、それを引っ張ってきています。
const {getDisplayMedia} = useMediaDevice();
getDisplayMediaではキャプチャする画面を選択すると MediaStream オブジェクトを返します。
const mediaStream = await getDisplayMedia();
MediaStreamオブジェクトはビデオなどのソース情報をストリーミング形式で流してくれる役割を果たすオブジェクトです。
なので、操作している画面がリアルタイムで共有されるわけですね。
あとはこのMediaStreamオブジェクトを <video>
タグの中に流し込んであげればストリーミングされている画面情報がビデオとして画面上で再生される…というわけです。
ちなみに、<video>
タグをVueで扱うには2点注意事項があります。
<video
:srcObject.prop="videoSrc"
autoplay
width="400"
/>
- MediaStreamオブジェクトは
srcObject.prop
に渡すこと autoplay
属性を付与しておくこと
srcObject.prop
と似た挙動を Vanilla JS で記述するとこうなります。
document.querySelector('CSS_SELECTOR').srcObject = MEDIA_STREAM_INSTANCE;
また、 autoplay
を付与しておかないと勝手にビデオが再生されません。
さて、あとは 読み込んでいる getDisplayMedia()
を確認してみましょう。
useMediaDevice.ts
という形で機能をファイルとして切り出しています。
const useMediaDevice = () => {
const cn = {video: true}; // 設定(constraints)
const getDisplayMedia = async (): Promise<MediaStream> => {
try {
const {mediaDevices} = navigator;
const stream:MediaStream = await mediaDevices.getDisplayMedia(cn); // navigator オブジェクトから取得
console.log(stream);
return stream;
} catch (e) {
console.error('Error: ' + e);
return new MediaStream();
}
};
return {
getDisplayMedia,
};
};
export default useMediaDevice;
こちらもわかりやすいですね。一番重要なのは下記の部分です。
const stream:MediaStream = await mediaDevices.getDisplayMedia(cn); // navigator オブジェクトから取得
getDisplayMediaを発火すると共有画面の選択画面がユーザに表示されます。ユーザが結果をクリックすると、Promiseの結果として MediaStream インストンスが発動します。
もしユーザが共有をしない(キャンセルボタンを押下)すると、例外処理のフローに遷移します。
ということで画面キャプチャの方法でした!ここからcanvas要素と組み合わせると色々面白いことができる…のですが、今回はここまで!