Pada artikel ini, Anda akan belajar bagaimana cara menambahkan video dan audio recorder ke aplikasi React menggunakan MediaRecorder API.
Di dunia saat ini, di mana pekerjaan jarak jauh dan hibrida menjadi semakin populer, organisasi perlu mengadopsi bentuk komunikasi asinkron dalam operasi sehari-hari mereka. Ini termasuk merekam atau membuat catatan rapat, menggunakan lebih banyak bentuk komunikasi teks, dll. Ada banyak aplikasi dengan kemampuan ini yang membuat komunikasi asinkron lebih mudah diadopsi.
Prasyarat
- Node.js sudah terinstal di komputer Anda
- Pengetahuan tentang JavaScript dan React
Menyusun aplikasi React yang baru
Pertama, kita akan membuat aplikasi React baru menggunakan Vite, sebuah alat bantu pembuatan JavaScript yang sangat cepat:
npm create vite@latest
Untuk menjawab pertanyaan yang mengikuti perintah:
- Ketik nama proyek (video-audio-recorder)
- Pilih React sebagai kerangka kerja
- Pilih varian Javascript
Selanjutnya, mari kita arahkan ke direktori proyek yang baru dibuat, instal dependensi yang diperlukan, dan jalankan server pengembangan menggunakan perintah berikut:
cd video-audio-recorder npm install npm run dev
Setelah selesai, sebuah server akan dimulai pada http://localhost:5173/. Mari kita buka URL pada peramban web.
API MediaRecorder
Untuk merekam audio atau video pada browser, diperlukan MediaStream. MediaStream adalah antarmuka yang merepresentasikan konten media dan terdiri dari trek audio dan video.
Untuk mendapatkan objek MediaStream, Anda dapat menggunakan konstruktor MediaStream() atau memanggil fungsi-fungsi berikut: MediaDevices.getUserMedia(), MediaDevices.getDisplayMedia(), atau HTMLCanvasElement.captureStream().
Untuk kepentingan tutorial ini, kita akan fokus pada fungsi MediaDevices.getuserMedia untuk membuat perekam video dan audio.
Membuat antarmuka aplikasi demo
Pada bagian ini, kita akan membuat antarmuka aplikasi demo.
Komponen audio recorder
Pertama, buat sebuah berkas di direktori src bernama AudioRecorder.jsx dan tempelkan isi blok kode berikut ini:
import { useState, useRef } from "react"; const AudioRecorder = () => { const [permission, setPermission] = useState(false); const [stream, setStream] = useState(null); const getMicrophonePermission = async () => { if ("MediaRecorder" in window) { try { const mediaStream = await navigator.mediaDevices.getUserMedia({ audio: true, video: false, }); setPermission(true); setStream(mediaStream); } catch (err) { alert(err.message); } } else { alert("The MediaRecorder API is not supported in your browser."); } }; return ( <div> <h2>Audio Recorder</h2> <main> <div className="audio-controls"> {!permission ? ( <button onClick={getMicrophonePermission} type="button"> Get Microphone </button> ) : null} {permission ? ( <button type="button"> Record </button> ) : null} </div> </main> </div> ); }; export default AudioRecorder;
Blok kode di atas melakukan hal berikut:
- Mendeklarasikan UI untuk komponen perekam audio
- Menerima izin mikrofon dari browser menggunakan fungsi getMicrophonePermission
- Menetapkan MediaStream yang diterima dari fungsi navigator.mediaDevices.getUserMedia ke variabel status stream (Kita akan segera menggunakannya)
Komponen video recorder
Selanjutnya, mari kita buat antarmuka untuk komponen video recorder. Masih dalam direktori src, buat file lain bernama VideoRecorder.jsx dan tempelkan isi blok kode di bawah ini:
import { useState, useRef } from "react"; const VideoRecorder = () => { const [permission, setPermission] = useState(false); const [stream, setStream] = useState(null); const getCameraPermission = async () => { if ("MediaRecorder" in window) { try { const streamData = await navigator.mediaDevices.getUserMedia({ audio: true, video: true, }); setPermission(true); setStream(streamData); } catch (err) { alert(err.message); } } else { alert("The MediaRecorder API is not supported in your browser."); } }; return ( <div> <h2>Video Recorder</h2> <main> <div className="video-controls"> {!permission ? ( <button onClick={getCameraPermission} type="button"> Get Camera </button> ):null} {permission ? ( <button type="button"> Record </button> ):null} </div> </main> </div> ); }; export default VideoRecorder;
Sama halnya dengan komponen audio recorder, blok kode di atas melakukan hal berikut:
- Mendeklarasikan UI untuk komponen perekam video
- Menerima izin mikrofon dari browser menggunakan fungsi getCameraPermission
- Menetapkan MediaStream yang diterima dari metode getUserMdia ke variabel status stream
Styling aplikasi kita
Kita tidak perlu menulis terlalu banyak kode untuk mempercantik aplikasi, karena sebagian besar penataan telah dilakukan selama perancah aplikasi.
Pada berkas index.css yang terletak di direktori src, tambahkan gaya berikut ini di bagian bawah:
... .button-flex { display: flex; justify-content: center; align-items: center; gap: 10px; } .audio-controls, .video-controls { margin-bottom: 20px; } .audio-player, .video-player, .recorded-player { display: flex; flex-direction: column; align-items: center; } .live-player { height: 200px; width: 400px; border: 1px solid #646cff; margin-bottom: 30px; } .recorded-player video { height: 400px; width: 800px; } ...
Kemudian, ubah nilai place-items pada elemen body dari center ke start
... body { margin: 0; display: flex; place-items: start; min-width: 320px; min-height: 100vh; } ...
Merender komponen
Untuk menampilkan komponen yang baru dibuat, buka App.jsx dan ganti isinya dengan blok kode berikut:
import "./App.css"; import { useState, useRef } from "react"; import VideoRecorder from "../src/VideoRecorder"; import AudioRecorder from "../src/AudioRecorder"; const App = () => { let [recordOption, setRecordOption] = useState("video"); const toggleRecordOption = (type) => { return () => { setRecordOption(type); }; }; return ( <div> <h1>React Media Recorder</h1> <div className="button-flex"> <button onClick={toggleRecordOption("video")}> Record Video </button> <button onClick={toggleRecordOption("audio")}> Record Audio </button> </div> <div> {recordOption === "video" ? <VideoRecorder /> : <AudioRecorder />} </div> </div> ); }; export default App;
Blok kode di atas merender komponen VideoRecorder atau AudioRecorder, tergantung opsi yang dipilih. Kembali ke browser, Anda akan mendapatkan hasil sebagai berikut:
Sekarang, setelah selesai, mari kita fokus pada peningkatan fungsionalitas komponen.
Peningkatan komponen: audio recorder
Agar audio recorder dapat berfungsi dengan baik, maka harus memenuhi persyaratan berikut ini:
- Menghentikan/memulai audio recorder
- Pemutaran dan pengunduhan audio
Menghentikan/memulai audio recorder
Mari kita mulai dengan mendeklarasikan variabel dan nilai status.
Pertama, di luar cakupan fungsi komponen (karena kita tidak memerlukannya untuk di-render ulang sebagai pembaruan status komponen), mari deklarasikan variabel mimeType:
... const mimeType = "audio/webm"; ...
Variabel ini menetapkan jenis file yang diinginkan. Pelajari lebih lanjut tentang tipe MIME di sini.
Selanjutnya, mari mendeklarasikan variabel state berikut ini di dalam lingkup komponen AudioRecorder:
... const [permission, setPermission] = useState(false); const mediaRecorder = useRef(null); const [recordingStatus, setRecordingStatus] = useState("inactive"); const [stream, setStream] = useState(null); const [audio, setAudio] = useState(null); const [audioChunks, setAudioChunks] = useState([]); ...
- permission menggunakan nilai Boolean untuk mengindikasikan apakah izin pengguna telah diberikan
- mediaRecorder menyimpan data dari pembuatan objek MediaRecorder baru, yang diberi MediaStream untuk direkam
- recordingStatus menetapkan status perekaman perekam saat ini. Tiga nilai yang mungkin adalah recording, incative, dan paused
- stream berisi MediaStream yang diterima dari metode getUserMedia
- audio berisi URL gumpalan ke rekaman audio yang sudah selesai
- audioChunks berisi potongan-potongan yang dikodekan (chunks) dari rekaman audio
Setelah itu, mari kita definisikan fungsi yang memungkinkan kita untuk memulai dan menghentikan rekaman.
Mari kita mulai dengan fungsi startRecording. Tepat setelah fungsi getMicrophonePermission, tambahkan kode berikut:
... const startRecording = async () => { setRecordingStatus("recording"); const media = new MediaRecorder(stream, { type: mimeType }); mediaRecorder.current = media; mediaRecorder.current.start(); let localAudioChunks = []; mediaRecorder.current.ondataavailable = (event) => { if (typeof event.data === "undefined") return; if (event.data.size === 0) return; localAudioChunks.push(event.data); }; setAudioChunks(localAudioChunks); }; ...
Selanjutnya, buat fungsi stopRecording di bawah fungsi startRecording.
const stopRecording = () => { setRecordingStatus("inactive"); mediaRecorder.current.stop(); mediaRecorder.current.onstop = () => { const audioBlob = new Blob(audioChunks, { type: mimeType }); const audioUrl = URL.createObjectURL(audioBlob); setAudio(audioUrl); setAudioChunks([]); }; };
Selanjutnya, mari kita modifikasi <div> dengan className “audio-controls” untuk membuat tombol mulai dan berhenti merekam secara kondisional, tergantung pada status recordingStatus:
<div className="audio-controls"> {!permission ? ( <button onClick={getMicrophonePermission} type="button"> Get Microphone </button> ) : null} {permission && recordingStatus === "inactive" ? ( <button onClick={startRecording} type="button"> Start Recording </button> ) : null} {recordingStatus === "recording" ? ( <button onClick={stopRecording} type="button"> Stop Recording </button> ) : null} </div>
Pemutaran dan pengunduhan audio
Untuk memutar file audio yang telah direkam, kita akan menggunakan tag audio HTML. Di bawah <div> dengan className “audio-controls”, mari tambahkan kode berikut:
... {audio ? ( <div className="audio-player"> <audio src={audio} controls></audio> <a download href={audio}> Download Recording </a> </div> ) : null} ...
Menautkan blob dari rekaman ke elemen anchor dan menambahkan atribut download agar membuatnya “dapat diunduh.”
Peningkatan komponen: video recorder
Agar perekam video menjadi lengkap, perekam video harus memenuhi persyaratan berikut ini:
- Umpan video waktu nyata
- Menghentikan/memulai perekaman video
- Pemutaran dan pengunduhan video
Real-time video feed
Kita perlu melihat bidang pandang kamera apabila kamera aktif untuk mengetahui area apa yang ditangkap dalam perekaman.
Pertama, mari kita tetapkan mimeType file yang diinginkan di luar cakupan fungsi komponen VideoRecorder:
... const mimeType = 'video/webm; codecs="opus,vp8"'; ...
Selanjutnya, mari kita mendefinisikan variabel state yang diperlukan. Kita akan kembali ke file VideoRecorder.jsx yang telah kita buat sebelumnya:
const [permission, setPermission] = useState(false); const mediaRecorder = useRef(null); const liveVideoFeed = useRef(null); const [recordingStatus, setRecordingStatus] = useState("inactive"); const [stream, setStream] = useState(null); const [recordedVideo, setRecordedVideo] = useState(null); const [videoChunks, setVideoChunks] = useState([]);
- permission menggunakan nilai Boolean untuk menunjukkan apakah izin pengguna telah diberikan
- liveVideoFeed berisi aliran video langsung dari kamera pengguna
- recordingStatus mengatur status perekaman perekam saat ini. Tiga nilai yang mungkin adalah recording, inactive, dan paused
- stream berisi MediaStream yang diterima dari metode getUserMedia
- videoChunks berisi potongan-potongan yang dikodekan (chunk) dari rekaman video
- recordedVideo berisi URL gumpalan ke rekaman video yang sudah selesai
Mari kita juga memodifikasi fungsi getCameraPermission menjadi seperti berikut ini:
... const getCameraPermission = async () => { setRecordedVideo(null); //get video and audio permissions and then stream the result media stream to the videoSrc variable if ("MediaRecorder" in window) { try { const videoConstraints = { audio: false, video: true, }; const audioConstraints = { audio: true }; // create audio and video streams separately const audioStream = await navigator.mediaDevices.getUserMedia( audioConstraints ); const videoStream = await navigator.mediaDevices.getUserMedia( videoConstraints ); setPermission(true); //combine both audio and video streams const combinedStream = new MediaStream([ ...videoStream.getVideoTracks(), ...audioStream.getAudioTracks(), ]); setStream(combinedStream); //set videostream to live feed player liveVideoFeed.current.srcObject = videoStream; } catch (err) { alert(err.message); } } else { alert("The MediaRecorder API is not supported in your browser."); } }; ...
Untuk mencegah mikrofon menyebabkan gema selama perekaman, kita akan membuat dua stream media yang terpisah, masing-masing untuk audio dan video, kemudian menggabungkan kedua stream menjadi satu. Dan terakhir, atur liveVideoFeed agar hanya berisi stream video.
Menghentikan dan memulai perekaman video
Mirip dengan perekam audio yang kita buat sebelumnya, kita akan mulai dengan membuat fungsi startRecording tepat di bawah fungsi getCameraPermission:
... const startRecording = async () => { setRecordingStatus("recording"); const media = new MediaRecorder(stream, { mimeType }); mediaRecorder.current = media; mediaRecorder.current.start(); let localVideoChunks = []; mediaRecorder.current.ondataavailable = (event) => { if (typeof event.data === "undefined") return; if (event.data.size === 0) return; localVideoChunks.push(event.data); }; setVideoChunks(localVideoChunks); }; ...
Berikutnya, kita akan membuat fungsi stopRecording tepat di bawah fungsi startRecording untuk menghentikan perekaman video:
... const stopRecording = () => { setPermission(false); setRecordingStatus("inactive"); mediaRecorder.current.stop(); mediaRecorder.current.onstop = () => { const videoBlob = new Blob(videoChunks, { type: mimeType }); const videoUrl = URL.createObjectURL(videoBlob); setRecordedVideo(videoUrl); setVideoChunks([]); }; }; ...
Pemutaran dan pengunduhan video
Untuk mengaktifkan pemutaran dan pengunduhan video, serta melihat semua perubahan yang telah kita buat sejauh ini, mari perbarui bagian HTML pada file komponen kita:
... <div> <h2>Video Recorder</h2> <main> <div className="video-controls"> {!permission ? ( <button onClick={getCameraPermission} type="button"> Get Camera </button> ) : null} {permission && recordingStatus === "inactive" ? ( <button onClick={startRecording} type="button"> Start Recording </button> ) : null} {recordingStatus === "recording" ? ( <button onClick={stopRecording} type="button"> Stop Recording </button> ) : null} </div> </main> <div className="video-player"> {!recordedVideo ? ( <video ref={liveVideoFeed} autoPlay className="live-player"></video> ) : null} {recordedVideo ? ( <div className="recorded-player"> <video className="recorded" src={recordedVideo} controls></video> <a download href={recordedVideo}> Download Recording </a> </div> ) : null} </div> </div> ...
Alternatif untuk membuat perekam video dan audio Anda sendiri
Daripada menulis semua kode ini untuk mengaktifkan perekaman audio dan video di aplikasi Anda, Anda mungkin ingin mempertimbangkan untuk menggunakan pustaka eksternal yang dioptimalkan dengan baik untuk apa yang ingin Anda capai.
Contoh yang populer adalah RecordRTC, sebuah pustaka JavaScript fleksibel yang menawarkan berbagai pilihan penyesuaian. Contoh lainnya termasuk react-media-recorder, react-video-recorder, dll.
Penutup
Pada tutorial ini, kita telah belajar bagaimana cara membuat perekam audio dan video kustom di React menggunakan API HTML MediaRecorder dan API MediaStream.