Merekam audio dan video dalam HTML5

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

HTML5 dapat membantu. Hal ini mungkin tidak terlihat, tetapi munculnya HTML5 telah membawa lonjakan akses ke hardware perangkat. Geolocation (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 getUserMedia() API adalah kisah yang menarik.

Beberapa varian "Media Capture API" telah berkembang selama beberapa tahun terakhir. Banyak orang menyadari perlunya dapat mengakses perangkat native di web, tetapi hal itu membuat semua orang dan ibunya menyusun spesifikasi baru. Semuanya menjadi sangat berantakan sehingga W3C akhirnya memutuskan untuk membentuk grup kerja. Tujuan utamanya? Pahami kegilaan ini. Kelompok Kerja Kebijakan API Perangkat (DAP) telah ditugaskan untuk menggabungkan + menstandarkan banyak proposal.

Saya akan mencoba meringkas apa yang terjadi pada tahun 2011…

Babak 1: Perekaman Media HTML

Perekaman Media HTML adalah upaya pertama DAP untuk menstandarkan perekaman media di web. Cara kerjanya adalah dengan membebani <input type="file"> 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">

Cukup bagus, bukan? Saya sangat menyukai bahwa kode ini menggunakan kembali input file. Secara semantik, hal ini masuk akal. Kekurangan "API" khusus ini adalah kemampuan untuk melakukan efek realtime (misalnya, merender data webcam live ke <canvas> dan menerapkan filter WebGL). Perekaman Media HTML 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 Seluler 10.0
  • Safari dan Chrome iOS6 (dukungan sebagian)

Babak 2: elemen perangkat

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

Opera adalah salah satu browser pertama yang membuat implementasi awal perekaman video berdasarkan elemen <device>. Segera setelahnya (tepatnya pada hari yang sama), WhatWG memutuskan untuk menghapus tag <device> dan memilih API JavaScript baru yang disebut navigator.getUserMedia(). Seminggu kemudian, Opera merilis build baru yang menyertakan dukungan untuk spesifikasi getUserMedia() yang diperbarui. Kemudian pada tahun itu, Microsoft 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>. Sepertinya ada satu API yang tidak perlu dikhawatirkan lagi :) <device> memang memiliki dua hal yang bagus untuknya: 1.) bersifat semantik, dan 2.) mudah diperluas untuk mendukung lebih dari sekadar perangkat audio/video.

Tarik napas. Hal ini bergerak cepat.

Babak 3: WebRTC

Elemen <device> akhirnya mengikuti jalan Dodo.

Kecepatan untuk menemukan API pengambilan 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 cara 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 diakses dengan panggilan, bukan penginstalan. Fitur ini terintegrasi langsung ke browser. Sudah tidak sabar?

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 ke 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 {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? Perekaman media adalah contoh sempurna dari API HTML5 baru yang bekerja sama. Fungsi ini berfungsi bersama dengan teman HTML5 kami yang lain, <audio> dan <video>. Perhatikan bahwa kita tidak menetapkan atribut src atau menyertakan elemen <source> pada elemen <video>. Daripada memasukkan URL ke file media ke video, kita menetapkan srcObject ke objek LocalMediaStream yang mewakili webcam.

Saya juga memberi tahu <video> ke autoplay, jika tidak, <video> akan dibekukan pada frame pertama. Menambahkan controls juga berfungsi seperti yang Anda harapkan.

Menetapkan batasan media (resolusi, tinggi, lebar)

Parameter pertama ke getUserMedia() juga dapat digunakan untuk menentukan lebih banyak persyaratan (atau batasan) pada streaming media yang ditampilkan. Misalnya, bukan 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 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 keren Sam Dutton tentang cara memungkinkan pengguna memilih sumber media.

Keamanan

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

Dialog izin di Chrome
Dialog izin di Chrome

Memberikan pengganti

Untuk pengguna yang tidak memiliki dukungan untuk navigator.mediaDevices.getUserMedia(), salah satu opsi adalah melakukan penggantian 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;
}