序文
みなさん、こんにちは。私は海の怪物です。
最近、プロジェクトでWebページに記録する必要が生じました。検索の波の後、react-media-recorder [1]ライブラリが見つかりました。今日は、このライブラリのソースコードを0から1まで調べて、React録画、ビデオ録画、および画面録画機能を実現しましょう。
完全なプロジェクトコードはGithubに配置されています[2]
ニーズとアイデア
まず、何を達成したいのかを明確にする必要があります。オーディオ録音、ビデオ録画、スクリーン録画です。
この記録メディアストリームの原理は、実際には非常に単純です。
覚えておいてください:入力をstream
保存blobList
し、最後にプレビューに変換しblobUrl
ます。
基本機能
上記の簡単なアイデアで、最初に簡単な録画およびビデオ録画機能を作成できます。
ここでは、最初に基本的なHTML構造を実装します。
const App = () => {
const [audioUrl, setAudioUrl] = useState<string>('');
const startRecord = async () => {}
const stopRecord = async () => {}
return (
<div>
<h1>react 录音</h1>
<audio src={audioUrl} controls />
<button onClick={startRecord}>开始</button>
<button>暂停</button>
<button>恢复</button>
<button onClick={stopRecord}>停止</button>
</div>
);
}
开始
上記の、、、および4つの関数暂停
に加えて、記録結果を表示するための関数があります。恢复
停止
<audio>
その後、達成するため开始
に停止
:
const medisStream = useRef<MediaStream>();
const recorder = useRef<MediaRecorder>();
const mediaBlobs = useRef<Blob[]>([]);
// 开始
const startRecord = async () => {
// 读取输入流
medisStream.current = await navigator.mediaDevices.getUserMedia({ audio: true, video: false });
// 生成 MediaRecorder 对象
recorder.current = new MediaRecorder(medisStream.current);
// 将 stream 转成 blob 来存放
recorder.current.ondataavailable = (blobEvent) => {
mediaBlobs.current.push(blobEvent.data);
}
// 停止时生成预览的 blob url
recorder.current.onstop = () => {
const blob = new Blob(mediaBlobs.current, { type: 'audio/wav' })
const mediaUrl = URL.createObjectURL(blob);
setAudioUrl(mediaUrl);
}
recorder.current?.start();
}
// 结束,不仅让 MediaRecorder 停止,还要让所有音轨停止
const stopRecord = async () => {
recorder.current?.stop()
medisStream.current?.getTracks().forEach((track) => track.stop());
}
上記からわかるように、最初にから入力ストリームをgetUserMedia
取得しmediaStream
、後で開いvideo: true
て。
次に、にmediaStream
渡して、データを現在のストリームに保存します。mediaRecorder
ondataavailable
blob
最後のステップは、プレビューリンクURL.createObjectURL
をです。このAPIは、フロントエンドで非常に便利です。たとえば、画像をアップロードするときに、実際に画像を送信せずに、画像のプレビューを実現するために呼び出すこともできます。プレビュー画像を表示するためのバックエンド。
开始
をクリックすると、現在のWebページが記録されていることがわかります。
次に、残りの部分と次の機能も暂停
実装します。恢复
const pauseRecord = async () => {
mediaRecorder.current?.pause();
}
const resumeRecord = async () => {
mediaRecorder.current?.resume()
}
フック
単純な関数を実装した後、上記の関数をReact Hooksにカプセル化して、最初にこれらすべてのロジックを関数にスローしてから、APIに戻りましょう。
const useMediaRecorder = () => {
const [mediaUrl, setMediaUrl] = useState<string>('');
const mediaStream = useRef<MediaStream>();
const mediaRecorder = useRef<MediaRecorder>();
const mediaBlobs = useRef<Blob[]>([]);
const startRecord = async () => {
mediaStream.current = await navigator.mediaDevices.getUserMedia({ audio: true, video: false });
mediaRecorder.current = new MediaRecorder(mediaStream.current);
mediaRecorder.current.ondataavailable = (blobEvent) => {
mediaBlobs.current.push(blobEvent.data);
}
mediaRecorder.current.onstop = () => {
const blob = new Blob(mediaBlobs.current, { type: 'audio/wav' })
const url = URL.createObjectURL(blob);
setMediaUrl(url);
}
mediaRecorder.current?.start();
}
const pauseRecord = async () => {
mediaRecorder.current?.pause();
}
const resumeRecord = async () => {
mediaRecorder.current?.resume()
}
const stopRecord = async () => {
mediaRecorder.current?.stop()
mediaStream.current?.getTracks().forEach((track) => track.stop());
mediaBlobs.current = [];
}
return {
mediaUrl,
startRecord,
pauseRecord,
resumeRecord,
stopRecord,
}
}
App.tsx
で戻り値を取得するだけです:
const App = () => {
const { mediaUrl, startRecord, resumeRecord, pauseRecord, stopRecord } = useMediaRecorder();
return (
<div>
<h1>react 录音</h1>
<audio src={mediaUrl} controls />
<button onClick={startRecord}>开始</button>
<button onClick={pauseRecord}>暂停</button>
<button onClick={resumeRecord}>恢复</button>
<button onClick={stopRecord}>停止</button>
</div>
);
}
パッケージ化したら、このフックに機能を追加します。
明確なデータ
blob URLを生成するとき、URL.createObjectURL
これを実現するためにAPIを呼び出します。生成されたURLは次のようになります。
blob:http://localhost:3000/e571f5b7-13bd-4c93-bc53-0c84049deb0a
URL.createObjectURL
の参照は毎回生成されurl -> blob
、そのような参照もリソースメモリを占有するため、この参照を破棄する方法を提供できます。
const useMediaRecorder = () => {
const [mediaUrl, setMediaUrl] = useState<string>('');
...
return {
...
clearBlobUrl: () => {
if (mediaUrl) {
URL.revokeObjectURL(mediaUrl);
}
setMediaUrl('');
}
}
}
スクリーンレコーディング
上記のオーディオおよびビデオの記録getUserMedia
は、画面の記録はgetDisplayMedia
このあります。
これら2つの状況をより適切に区別するために、開発者にaudio
、video
およびscreen
3つのパラメーターを提供して、対応する入力ストリームデータを取得するために呼び出すインターフェイスを指定できます。
const useMediaRecorder = (params: Params) => {
const {
audio = true,
video = false,
screen = false,
askPermissionOnMount = false,
} = params;
const [mediaUrl, setMediaUrl] = useState<string>('');
const mediaStream = useRef<MediaStream>();
const audioStream = useRef<MediaStream>();
const mediaRecorder = useRef<MediaRecorder>();
const mediaBlobs = useRef<Blob[]>([]);
const getMediaStream = useCallback(async () => {
if (screen) {
// 录屏接口
mediaStream.current = await navigator.mediaDevices.getDisplayMedia({ video: true });
mediaStream.current?.getTracks()[0].addEventListener('ended', () => {
stopRecord()
})
if (audio) {
// 添加音频输入流
audioStream.current = await navigator.mediaDevices.getUserMedia({ audio: true })
audioStream.current?.getAudioTracks().forEach(audioTrack => mediaStream.current?.addTrack(audioTrack));
}
} else {
// 普通的录像、录音流
mediaStream.current = await navigator.mediaDevices.getUserMedia(({ video, audio }))
}
}, [screen, video, audio])
// 开始录
const startRecord = async () => {
// 获取流
await getMediaStream();
mediaRecorder.current = new MediaRecorder(mediaStream.current!);
mediaRecorder.current.ondataavailable = (blobEvent) => {
mediaBlobs.current.push(blobEvent.data);
}
mediaRecorder.current.onstop = () => {
const [chunk] = mediaBlobs.current;
const blobProperty: BlobPropertyBag = Object.assign(
{ type: chunk.type },
video ? { type: 'video/mp4' } : { type: 'audio/wav' }
);
const blob = new Blob(mediaBlobs.current, blobProperty)
const url = URL.createObjectURL(blob);
setMediaUrl(url);
onStop(url, mediaBlobs.current);
}
mediaRecorder.current?.start();
}
...
}
ユーザーがビデオとサウンドを記録できるようにしたため、URLを生成するときに、対応するメディアタイプblobProperty
をblobUrl
。
最後に、フックが呼び出されるscreen: true
と、画面記録機能をオンにできます。
注:ビデオ録画、オーディオ録画、画面録画のいずれであっても、システムを呼び出す機能であり、Webページはブラウザにこの機能を要求するだけですが、ブラウザがすでにシステム権限を持っていることが前提です。システム設定での閲覧を許可する必要があります。デバイスには、画面を記録するためのこれらの権限があります。
メディアストリームを取得するロジックをgetMediaStream
関数ユーザーのアクセス許可を取得するために簡単に使用できます。コンポーネントがロードされた直後にユーザーのカメラ、マイク、および画面の記録のアクセス許可を取得する場合は、これを使用できます。でuseEffect
。それを呼び出します:
useEffect(() => {
if (askPermissionOnMount) {
getMediaStream().then();
}
}, [audio, screen, video, getMediaStream, askPermissionOnMount])
プレビュー
ビデオ録画を実現するにgetUserMedia
は、その。{ video: true }
ユーザーが録画中にエフェクトをより見やすくするために、ビデオストリームをユーザーに返すこともできます。
return {
...
getMediaStream: () => mediaStream.current,
getAudioStream: () => audioStream.current
}
これらを取得した後mediaStream
、はsrcObject
プレビュー用に直接割り当てることができます。
<button onClick={() => previewVideo.current!.srcObject = getMediaStream() || null}>
预览
</button>
ミュート
最後に、ミュート機能を実装しましょう。原理も同様に単純です。audioStream
内部を取得し、それらaudioTrack
を設定します。enabled = false
const toggleMute = (isMute: boolean) => {
mediaStream.current?.getAudioTracks().forEach(track => track.enabled = !isMute);
audioStream.current?.getAudioTracks().forEach(track => track.enabled = !isMute)
setIsMuted(isMute);
}
使用時にチャネルを無効または有効にするために使用できます。
<button onClick={() => toggleMute(!isMuted)}>{isMuted ? '打开声音' : '禁音'}</button>
要約する
上記では、WebRTC APIを使用して、録画、ビデオ、および画面録画ツールのフックを実装しています。簡単な要約は次のとおりです。
getUserMedia
カメラだけでなくマイクからのストリームを取得するために使用できますgetDisplayMedia
画面のビデオおよびオーディオストリームを取得するために使用されます物事を記録することの本質は、データを取得するため
stream -> blobList -> blob url
にそれらを監視MediaRecorder
できることですstream
blob
MediaRecorder
また、開始、終了、一時停止、再開などの複数のレコード関連のインターフェイスも提供しますcreateObjectURL
とrevokeObjectURL
は反意語であり、一方は参照を作成し、もう一方は破棄しますミューティングは、オーディオトラックを
track.enabled = false
閉じる
この小さなツールライブラリの実装はここにあります。詳細については、react-media-recorder [3]のソースコードを確認できます。このライブラリは非常にシンプルで理解しやすいです。ソースコードを読み始めました!
参考文献
[1]
react-media-recorder:https ://github.com/0x006F/react-media-recorder
[2]プロジェクトコード:https ://github.com/haixiangyan/react-media-recorder
[3]react-media-recorder:https ://github.com/0x006F/react-media-recorder