Merekam audio dan video dalam HTML5

Pengantar

Perekaman Audio/Video telah lama menjadi "Holy Grail" dalam pengembangan web. Selama bertahun-tahun, kita harus mengandalkan plugin browser (Flash atau Silverlight) untuk menyelesaikan pekerjaan. Ayo!

HTML5 dapat membantu. Mungkin tidak terlihat, tetapi kemunculan HTML5 telah membawa lonjakan akses ke hardware perangkat. Geolokasi (GPS), Orientation API (akselerometer), WebGL (GPU), dan Web Audio API (hardware audio) adalah contoh yang sempurna. Fitur ini sangat canggih, mengekspos JavaScript API tingkat tinggi yang berada di atas kemampuan hardware yang mendasari sistem.

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

Jalan menuju getUserMedia()

Jika Anda tidak mengetahui sejarahnya, cara kami sampai pada API getUserMedia() adalah kisah yang menarik.

Beberapa varian "Media Capture API" telah berkembang selama beberapa tahun terakhir. Banyak orang menyadari perlunya kemampuan untuk mengakses perangkat native di web, tetapi hal itu membuat semua orang dan ibu mereka menyusun spesifikasi baru. Keadaan menjadi sangat kacau sehingga W3C akhirnya memutuskan untuk membentuk kelompok kerja. Tujuan satu-satunya? Pahami kekacauan ini. Device APIs Policy (DAP) Working Group telah ditugaskan untuk menggabungkan + menstandarkan berbagai proposal.

Kami akan mencoba merangkum apa yang terjadi pada tahun 2011…

Babak 1: HTML Media Capture

HTML Media Capture adalah upaya pertama DAP untuk menstandarkan pengambilan media di web. Cara kerjanya adalah dengan membebani <input type="file"> secara berlebihan dan menambahkan nilai baru untuk parameter accept.

Jika Anda ingin mengizinkan pengguna mengambil snapshot diri mereka sendiri dengan webcam, hal itu dapat dilakukan dengan capture=camera:

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

Merekam video atau audio juga serupa:

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

Bagus, kan? Saya sangat menyukai penggunaan kembali input file. Secara semantik, hal ini sangat masuk akal. Kekurangan "API" khusus ini adalah kemampuannya untuk melakukan efek real-time (misalnya, merender data webcam live ke <canvas> dan menerapkan filter WebGL). HTML Media Capture hanya memungkinkan Anda merekam file media atau mengambil snapshot pada waktu tertentu.

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 parsial)

Babak 2: Elemen perangkat

Banyak yang menganggap HTML Media Capture terlalu terbatas, sehingga muncul spesifikasi baru yang mendukung semua jenis perangkat (di masa mendatang). Tidak mengherankan, desain tersebut memerlukan elemen baru, yaitu elemen <device>, yang menjadi pendahulu getUserMedia().

Opera adalah salah satu browser pertama yang membuat implementasi awal perekaman video berdasarkan elemen <device>. Tidak lama setelah itu (pada hari yang sama, tepatnya), WhatWG memutuskan untuk menghapus tag <device> dan menggantinya dengan teknologi baru yang sedang naik daun, kali ini API JavaScript yang disebut navigator.getUserMedia(). Seminggu kemudian, Opera merilis build baru yang menyertakan dukungan untuk spesifikasi getUserMedia() yang diperbarui. Di tahun yang sama, Microsoft ikut bergabung dengan merilis Lab untuk IE9 yang mendukung spesifikasi baru.

Berikut tampilan <device>:

<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>. Satu API yang tidak perlu dikhawatirkan lagi :) <device> memiliki dua hal hebat: 1.) semantik, dan 2.) mudah diperluas untuk mendukung lebih dari sekadar perangkat audio/video.

Tarik napas. Hal ini berkembang pesat.

Babak 3: WebRTC

Elemen <device> akhirnya tidak digunakan lagi.

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

getUserMedia() terkait dengan WebRTC karena merupakan gateway ke kumpulan API tersebut. API 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. Akses kamera kini dapat dilakukan melalui panggilan, bukan penginstalan. Fitur ini disertakan langsung di browser. Penasaran?

Deteksi fitur

Deteksi fitur adalah pemeriksaan sederhana untuk 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 Anda akses. Misalnya, jika Anda ingin mengakses webcam, parameter pertama harus berupa {video: true}. Untuk menggunakan mikrofon dan kamera, teruskan {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 di sini? Perekaman media adalah contoh sempurna dari API HTML5 baru yang bekerja sama. Elemen ini berfungsi bersama dengan elemen HTML5 lainnya, <audio> dan <video>. Perhatikan bahwa kita tidak menetapkan atribut src atau menyertakan elemen <source> pada elemen <video>. Alih-alih memberikan URL ke file media ke video, kita menetapkan srcObject ke objek LocalMediaStream yang mewakili webcam.

Saya juga memberi tahu <video> untuk autoplay, jika tidak, video akan berhenti di frame pertama. Menambahkan controls juga berfungsi seperti yang Anda harapkan.

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, selain menunjukkan bahwa Anda menginginkan akses dasar ke video (misalnya, {video: true}), Anda juga dapat mewajibkan streaming dalam kualitas 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 constraints API.

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 diselesaikan 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 menarik dari Sam Dutton tentang cara mengizinkan pengguna memilih sumber media.

Keamanan

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

Dialog izin di Chrome
Dialog izin di Chrome

Menyediakan penggantian

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

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