HTML5 ile ses ve video yakalama

Giriş

Ses/video yakalama, uzun süredir web geliştiriciliğinin "Kutsal Kase"si olmuştur. Uzun yıllar boyunca bu işi yapmak için tarayıcı eklentilerine (Flash veya Silverlight) güvenmek zorunda kaldık. Hadi!

HTML5 imdadınıza yetişiyor. Çok belirgin olmasa da HTML5'in yükselişi, cihaz donanımına erişimde büyük bir artış sağladı. Coğrafi konum (GPS), Orientation API (ivme ölçer), WebGL (GPU) ve Web Audio API (ses donanımı) buna mükemmel örneklerdir. Bu özellikler, sistemin temel donanım özelliklerinin üzerinde yer alan üst düzey JavaScript API'lerini kullanıma sunarak inanılmaz derecede güçlü bir deneyim sunar.

Bu eğitimde, web uygulamalarının kullanıcının kamerasına ve mikrofonuna erişmesine olanak tanıyan yeni bir API olan GetUserMedia tanıtılmaktadır.

getUserMedia()'ya giden yol

Geçmişini bilmiyorsanız getUserMedia() API'sine nasıl ulaştığımızın ilginç bir hikayesi var.

Son birkaç yılda "Media Capture API'leri"nin çeşitli varyantları geliştirildi. Birçok kişi, web'de yerel cihazlara erişebilme ihtiyacını fark etti ancak bu durum, herkesin yeni bir spesifikasyon oluşturmasına neden oldu. İşler o kadar karıştı ki W3C sonunda bir çalışma grubu oluşturmaya karar verdi. Tek amaçları ne mi? Bu çılgınlığa bir anlam verin. Cihaz API'leri Politikası (DAP) Çalışma Grubu, çok sayıda teklifi birleştirip standartlaştırmakla görevlendirilmiştir.

2011'de neler olduğunu özetlemeye çalışacağım…

1. tur: HTML Media Capture

HTML Media Capture, DAP'nin web'de medya yakalamayı standartlaştırmaya yönelik ilk girişimiydi. <input type="file"> parametresini aşırı yükleyerek ve accept parametresi için yeni değerler ekleyerek çalışır.

Kullanıcıların web kamerasıyla kendi fotoğraflarını çekmelerine izin vermek istiyorsanız capture=camera ile bunu yapabilirsiniz:

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

Video veya ses kaydetme işlemi de benzerdir:

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

Güzel bir özellik, değil mi? Özellikle dosya girişini yeniden kullanması hoşuma gidiyor. Anlamsal olarak bu çok mantıklı. Bu "API"nin yetersiz kaldığı nokta, gerçek zamanlı efektler uygulama özelliğidir (ör. canlı web kamerası verilerini <canvas> öğesine oluşturma ve WebGL filtreleri uygulama). HTML Media Capture, yalnızca bir medya dosyası kaydetmenize veya anlık görüntü almanıza olanak tanır.

Destek:

  • Android 3.0 tarayıcı: İlk uygulamalardan biri. Nasıl çalıştığını görmek için bu videoyu izleyin.
  • Android için Chrome (0.16)
  • Firefox Mobile 10.0
  • iOS6 Safari ve Chrome (kısmi destek)

2. tur: Cihaz öğesi

Birçok kişi HTML Media Capture'ın çok sınırlayıcı olduğunu düşündü. Bu nedenle, her tür (gelecekteki) cihazı destekleyen yeni bir spesifikasyon ortaya çıktı. Tasarım, şaşırtıcı olmayan bir şekilde, getUserMedia()'ün öncülü olan <device> öğesi adında yeni bir öğe gerektiriyordu.

Opera, <device> öğesine dayalı video yakalamanın ilk uygulamalarını oluşturan ilk tarayıcılar arasındaydı. Kısa bir süre sonra (tam olarak aynı gün), WhatWG, <device> etiketini kaldırıp yerine başka bir yeni teknolojiyi, bu kez navigator.getUserMedia() adlı bir JavaScript API'yi kullanmaya karar verdi. Bir hafta sonra Opera, güncellenmiş getUserMedia() spesifikasyonunu destekleyen yeni derlemeler yayınladı. Aynı yılın ilerleyen dönemlerinde Microsoft da yeni spesifikasyonu destekleyen IE9 için Lab'ı yayınlayarak bu kervana katıldı.

<device> şu şekilde görünürdü:

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

Destek:

Maalesef yayınlanan hiçbir tarayıcıda <device> yoktu. Sanırım artık endişelenmeniz gereken bir API daha azaldı. :) <device>, iki önemli avantaja sahipti: 1.) Semantikti ve 2.) Yalnızca ses/video cihazlarından daha fazlasını destekleyecek şekilde kolayca genişletilebiliyordu.

Rahat olun. Bu işler çok hızlı ilerliyor.

3. tur: WebRTC

<device> öğesi de zamanla tarihe karıştı.

Daha kapsamlı WebRTC (Web Tabanlı Gerçek Zamanlı İletişim) çalışmaları sayesinde uygun bir yakalama API'si bulma süreci hızlandı. Bu spesifikasyon, W3C WebRTC Çalışma Grubu tarafından denetlenir. Google, Opera, Mozilla ve diğer birkaç şirketin uygulamaları vardır.

getUserMedia(), WebRTC ile ilgilidir çünkü bu API grubuna açılan ağ geçididir. Kullanıcının yerel kamera/mikrofon akışına erişim imkanı sağlar.

Destek:

getUserMedia(), Chrome 21, Opera 18 ve Firefox 17'den beri desteklenmektedir.

Başlarken

navigator.mediaDevices.getUserMedia() sayesinde artık web kamerası ve mikrofon girişini eklenti olmadan kullanabiliyoruz. Kamera erişimi için artık uygulama yüklemeniz gerekmez. Doğrudan tarayıcıya entegre edilmiştir. Heyecanlandınız mı?

Özellik algılama

Özellik algılama, navigator.mediaDevices.getUserMedia'nin varlığına yönelik basit bir kontroldür:

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

Giriş cihazına erişme

Web kamerasını veya mikrofonu kullanmak için izin istememiz gerekiyor. navigator.mediaDevices.getUserMedia() için ilk parametre, erişmek istediğiniz her medya türünün ayrıntılarını ve gereksinimlerini belirten bir nesnedir. Örneğin, web kamerasına erişmek istiyorsanız ilk parametre {video: true} olmalıdır. Hem mikrofonu hem de kamerayı kullanmak için {video: true, audio: true} değerini iletin:

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

Tamam. Peki burada ne oluyor? Medya yakalama, yeni HTML5 API'lerinin birlikte çalışmasına mükemmel bir örnektir. Diğer HTML5 dostlarımız <audio> ve <video> ile birlikte çalışır. <video> öğesinde src özelliğini ayarlamadığımızı veya <source> öğelerini eklemediğimizi fark edeceksiniz. Videoya bir medya dosyasının URL'sini beslemek yerine, srcObject öğesini web kamerasını temsil eden LocalMediaStream nesnesine ayarlıyoruz.

Ayrıca <video> öğesine autoplay talimatını veriyorum. Aksi takdirde ilk karede donar. controls eklediğinizde de beklendiği gibi çalışır.

Medya kısıtlamalarını (çözünürlük, yükseklik, genişlik) ayarlama

getUserMedia() için ilk parametre, döndürülen medya akışıyla ilgili daha fazla koşul (veya kısıtlama) belirtmek için de kullanılabilir. Örneğin, yalnızca videoya temel erişim istediğinizi belirtmek (ör. {video: true}) yerine, yayının HD olmasını da zorunlu kılabilirsiniz:

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

Daha fazla yapılandırma için constraints API'ye bakın.

Medya kaynağı seçme

MediaDevices arayüzünün enumerateDevices() yöntemi, mikrofon, kamera, kulaklık gibi kullanılabilir medya giriş ve çıkış cihazlarının listesini ister. Döndürülen Promise, cihazları açıklayan bir MediaDeviceInfo nesne dizisiyle çözümlenir.

Bu örnekte, bulunan son mikrofon ve kamera, medya akışı kaynağı olarak seçilmiştir:

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);
}

Kullanıcıların medya kaynağını seçmesine nasıl izin verileceğini gösteren Sam Dutton'ın mükemmel demosuna göz atın.

Güvenlik

Tarayıcılar, navigator.mediaDevices.getUserMedia() çağrıldığında bir izin iletişim kutusu gösterir. Bu iletişim kutusunda kullanıcılara kamera/mikrofon erişimine izin verme veya erişimi reddetme seçeneği sunulur. Örneğin, Chrome'un izin iletişim kutusu şu şekildedir:

Chrome'da izin iletişim kutusu
Chrome'da izin iletişim kutusu

Yedek sağlama

navigator.mediaDevices.getUserMedia() desteği olmayan kullanıcılar için API desteklenmiyorsa ve/veya çağrı bir nedenle başarısız olursa mevcut bir video dosyasına geri dönme seçeneği kullanılabilir:

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