Merekam audio dan video dalam HTML5

Pengantar

Perekaman Audio/Video telah menjadi the "Cawan Suci" dalam pengembangan web untuk waktu yang lama. Selama bertahun-tahun kita harus mengandalkan plugin browser (Flash atau Silverlight) untuk menyelesaikan pekerjaan. Ayo!

HTML5 dapat diselamatkan. Mungkin tidak jelas, tetapi kemunculan HTML5 telah membawa lonjakan akses ke perangkat keras. Geolokasi (GPS), Orientation API (akselerometer), WebGL (GPU), dan Web Audio API (hardware audio) merupakan contoh yang bagus. Fitur-fitur ini sangat ampuh, dan mengekspos JavaScript API tingkat tinggi yang di atas kemampuan perangkat keras yang mendasari sistem.

Tutorial ini memperkenalkan API baru, GetUserMedia, yang memungkinkan aplikasi web untuk mengakses kamera dan mikrofon pengguna.

Jalan menuju getUserMedia()

Jika Anda tidak mengetahui sejarahnya, cara kita sampai di getUserMedia() API merupakan kisah yang menarik.

Beberapa varian "Media Capture API" telah berkembang selama beberapa tahun terakhir. Banyak orang menyadari kebutuhan untuk dapat mengakses perangkat asli di web, tetapi yang membuat semua orang dan ibu mereka menyusun spesifikasi baru. Banyak hal yang didapatkan sangat berantakan sehingga W3C akhirnya memutuskan untuk membentuk kelompok kerja. Satu-satunya tujuan mereka? Masuk akal! Grup Kerja Kebijakan API Perangkat (DAP) ditugaskan untuk mengonsolidasikan + menstandarkan banyak proposal.

Saya akan mencoba merangkum apa yang terjadi di tahun 2011...

Tahap 1: Pengambilan Media HTML

HTML Media Capture adalah upaya pertama DAP standarisasi pengambilan media di web. Fungsi ini berfungsi dengan membebani <input type="file"> dan menambahkan nilai baru untuk parameter accept.

Jika Anda ingin membiarkan pengguna mengambil foto diri mereka dengan {i>webcam<i}, yang mungkin dilakukan dengan capture=camera:

<input type="file" accept="image/*;capture=camera">

Perekaman video atau audio mirip:

<input type="file" accept="video/*;capture=camcorder">
<input type="file" accept="audio/*;capture=microphone">

Bagus, kan? Saya sangat menyukainya karena menggunakan input file. Secara semantik, sudah dapat diterapkan. Di mana "API" khusus ini gagal adalah kemampuan untuk melakukan efek realtime (misalnya, merender data webcam langsung ke <canvas> dan menerapkan filter WebGL). HTML Media Capture hanya memungkinkan Anda untuk merekam file media atau mengambil cuplikan dengan tepat waktu.

Dukungan:

  • Browser Android 3.0 - salah satu implementasi pertama. Tonton video ini untuk melihat cara kerjanya.
  • Chrome untuk Android (0.16)
  • Firefox Mobile 10.0
  • iOS6 Safari dan Chrome (dukungan sebagian)

Putaran 2: elemen perangkat

Banyak orang berpikir bahwa HTML Media Capture terlalu membatasi, jadi spesifikasi baru yang mendukung semua jenis perangkat (di masa mendatang). Tidak mengherankan, desain yang disebut untuk elemen baru, elemen <device>, yang menjadi pendahulu getUserMedia().

Opera adalah salah satu browser pertama yang membuat implementasi awal perekaman video berdasarkan elemen <device>. Segera setelah (tepatnya hari yang sama), WhatWG memutuskan untuk menghapus tag <device> dan menggantinya dengan yang lain, kali ini JavaScript API yang disebut navigator.getUserMedia(). Seminggu kemudian, Opera mengeluarkan bangunan baru yang mencakup dukungan untuk spesifikasi getUserMedia() yang diperbarui. Pada akhir tahun itu, Microsoft ikut serta dengan merilis Lab untuk IE9 yang mendukung spesifikasi baru ini.

Tampilan <device> akan terlihat seperti berikut:

<device type="media" onchange="update(this.data)"></device>
<video autoplay></video>
<script>
  function update(stream) {
    document.querySelector('video').src = stream.url;
  }
</script>

Dukungan:

Sayangnya, tidak ada browser yang dirilis yang menyertakan <device>. Saya rasa, ada satu API yang tidak perlu dikhawatirkan :) <device> memiliki dua hal hebat untuk itu: 1.) bersifat semantik, dan 2.) mudah diperluas untuk mendukung lebih dari sekadar perangkat audio/video.

Ambil napas. Fitur ini bergerak cepat!

Putaran 3: WebRTC

Elemen <device> akhirnya menggantikan Dodo.

Kecepatan untuk menemukan API perekaman yang sesuai dipercepat berkat upaya WebRTC (Web Real Time Communications) yang lebih besar. Spesifikasi tersebut diawasi oleh W3C WebRTC Working Group. Google, Opera, Mozilla, dan beberapa aplikasi lainnya memiliki implementasi.

getUserMedia() terkait dengan WebRTC karena merupakan gateway ke kumpulan API tersebut. Layanan ini menyediakan sarana untuk mengakses streaming kamera/mikrofon lokal pengguna.

Dukungan:

getUserMedia() telah didukung sejak Chrome 21, Opera 18, dan Firefox 17.

Memulai

Dengan navigator.mediaDevices.getUserMedia(), kita akhirnya dapat memanfaatkan input webcam dan mikrofon tanpa plugin. Kini akses kamera tinggal panggilan, bukan penginstalan jauh. Data tersebut disimpan langsung ke dalam browser. Anda sudah bersemangat?

Deteksi fitur

Pendeteksian fitur adalah pemeriksaan sederhana untuk mengetahui keberadaan navigator.mediaDevices.getUserMedia:

if (navigator.mediaDevices?.getUserMedia) {
  // Good to go!
} else {
  alert("navigator.mediaDevices.getUserMedia() is not supported");
}

Mendapatkan akses ke perangkat input

Untuk menggunakan webcam atau mikrofon, kami perlu meminta izin. Parameter pertama untuk navigator.mediaDevices.getUserMedia() adalah objek yang menentukan detail dan persyaratan untuk setiap jenis media yang ingin diakses. Misalnya, jika Anda ingin mengakses webcam, parameter pertama harus {video: true}. Untuk menggunakan mikrofon dan kamera, melalui {video: true, audio: true}:

<video autoplay></video>

<script>
  navigator.mediaDevices
    .getUserMedia({ video: true, audio: true })
    .then((localMediaStream) => {
      const video = document.querySelector("video");
      video.srcObject = localMediaStream;
    })
    .catch((error) => {
      console.log("Rejected!", error);
    });
</script>

Oke. Jadi apa yang terjadi? Pengambilan media adalah contoh sempurna dari API HTML5 baru dan bekerja sama. Ini berfungsi bersama dengan teman HTML5 kita yang lain, <audio> dan <video>. Perhatikan bahwa kita tidak menetapkan atribut src atau menyertakan elemen <source> pada elemen <video>. Alih-alih memasok URL ke video media, kita mengatur srcObject ke objek LocalMediaStream yang mewakili webcam.

Saya juga memberi tahu <video> kepada autoplay, jika tidak, kode akan dibekukan di {i>frame<i} pertama. Menambahkan controls juga berfungsi seperti yang diharapkan.

Menetapkan batasan media (resolusi, tinggi, lebar)

Parameter pertama untuk getUserMedia() juga dapat digunakan untuk menentukan lebih banyak persyaratan (atau batasan) pada streaming media yang ditampilkan. Misalnya, alih-alih hanya menunjukkan bahwa Anda menginginkan akses dasar ke video (misalnya, {video: true}), Anda juga dapat mewajibkan streaming menjadi HD:

const hdConstraints = {
  video: { width: { exact:  1280} , height: { exact: 720 } },
};

const stream = await navigator.mediaDevices.getUserMedia(hdConstraints);
const vgaConstraints = {
  video: { width: { exact:  640} , height: { exact: 360 } },
};

const stream = await navigator.mediaDevices.getUserMedia(hdConstraints);

Untuk konfigurasi lainnya, lihat API batasan.

Memilih sumber media

Metode enumerateDevices() dari antarmuka MediaDevices meminta daftar perangkat input dan output media yang tersedia, seperti mikrofon, kamera, headset, dan sebagainya. Promise yang ditampilkan di-resolve dengan array objek MediaDeviceInfo yang mendeskripsikan perangkat.

Dalam contoh ini, mikrofon dan kamera terakhir yang ditemukan dipilih sebagai sumber streaming media:

if (!navigator.mediaDevices?.enumerateDevices) {
  console.log("enumerateDevices() not supported.");
} else {
  // List cameras and microphones.
  navigator.mediaDevices
    .enumerateDevices()
    .then((devices) => {
      let audioSource = null;
      let videoSource = null;

      devices.forEach((device) => {
        if (device.kind === "audioinput") {
          audioSource = device.deviceId;
        } else if (device.kind === "videoinput") {
          videoSource = device.deviceId;
        }
      });
      sourceSelected(audioSource, videoSource);
    })
    .catch((err) => {
      console.error(`${err.name}: ${err.message}`);
    });
}

async function sourceSelected(audioSource, videoSource) {
  const constraints = {
    audio: { deviceId: audioSource },
    video: { deviceId: videoSource },
  };
  const stream = await navigator.mediaDevices.getUserMedia(constraints);
}

Lihat demo hebat Sam Dutton tentang cara agar pengguna dapat memilih sumber media.

Keamanan

Browser akan menampilkan dialog izin saat memanggil navigator.mediaDevices.getUserMedia(), yang memberi pengguna opsi untuk memberikan atau menolak akses ke kamera/mikrofon mereka. Misalnya, di sini adalah dialog izin Chrome:

Dialog izin di Chrome
Dialog izin di Chrome

Menyediakan penggantian

Bagi pengguna yang tidak memiliki dukungan untuk navigator.mediaDevices.getUserMedia(), salah satu opsinya adalah melakukan penggantian ke file video yang sudah ada jika API tidak didukung dan/atau panggilan gagal karena beberapa alasan:

if (!navigator.mediaDevices?.getUserMedia) {
  video.src = "fallbackvideo.webm";
} else {
  const stream = await navigator.mediaDevices.getUserMedia({ video: true });
  video.srcObject = stream;
}