Tarayıcıda kamera ve mikrofona erişmek için navigator.mediaDevices.getUserMedia(constraints) çağrılır. Fonksiyon kullanıcıdan izin ister ve bir Promise döndürür; izin verilirse söz, kamera ve/veya mikrofon izlerini (track) içeren bir MediaStream ile çözülür. Kullanıcı reddederse NotAllowedError, uygun cihaz yoksa NotFoundError ile reddedilir. Bu API yalnızca güvenli bağlamda (HTTPS, localhost veya file:///) çalışır; güvensiz bir sayfada navigator.mediaDevices undefined olur.
Bu yazıda kısıtlar (constraints) nesnesini, ideal, min, max, exact farkını, akışı <video> elemanında göstermeyi, kamerayı kapatmayı, hata adlarını, iframe ve Permissions Policy kurallarını, kameradan fotoğraf almayı ve MediaRecorder ile kayıt yapmayı göreceksiniz. Örnekler MDN belgelerine göre yazıldı; kamera ve mikrofon gerektirdikleri için çalıştırılmadı ve çıktı yorumu yalnızca MDN'nin gösterdiği yerlerde var.
İlk adım: akışı almak ve video elemanında göstermek
<video autoplay playsinline muted></video>
<button id="ac" type="button">Kamerayı aç</button>const video = document.querySelector("video");
document.querySelector("#ac").addEventListener("click", async () => {
try {
const stream = await navigator.mediaDevices.getUserMedia({
audio: false,
video: { width: 1280, height: 720 },
});
video.srcObject = stream;
video.onloadedmetadata = () => {
video.play();
};
} catch (err) {
console.error(`${err.name}: ${err.message}`);
}
});Akış srcObject özelliğine atanır ve meta veri yüklenince video oynatılır; MDN'nin "Width and height" örneği tam olarak bu yapıyı kullanır. Hata yakalamada err.name değerini yazdırmak önemlidir, çünkü hangi sorunla karşılaştığınızı bu ad söyler. MDN ayrıca sözün hiç çözülmeyip hiç reddedilmeyebileceğini de not eder: kullanıcının izin isteğine cevap vermesi zorunlu değildir ve isteği görmezden gelebilir. Arayüzünüz "izin bekleniyor" durumunu sonsuza kadar gösterebilecek şekilde tasarlanmalıdır. async/await söz dizimi async/await yazısında anlatılıyor.
Kısıtlar nesnesi: audio ve video

constraints nesnesinin iki üyesi vardır: video ve audio. En az biri belirtilmelidir. Her biri bir boolean ya da bir nesne olabilir ve varsayılan değerleri false olur. MDN kuralları şöyle özetler:
trueverilen tür, dönen akışta bulunmak zorundadır; bulunamazsa söz reddedilir.falseverilen tür akışta bulunmamalıdır.- İkisi de varsayılan olarak
falseolduğu için nesnede hiçbiri yoksa ya da nesne hiç verilmemişse söz her zaman reddedilir. - Nesne verilirse
MediaTrackConstraintssözlüğü olarak okunur:width,height,frameRate,facingMode,deviceIdgibi alanlar içerir.
ideal, min, max ve exact
Düz bir değer, örneğin width: 1280, bir tercihtir; MDN'ye göre düz değerler doğası gereği "ideal" sayılır. Tarayıcı bu değere en yakın ayarı bulmaya çalışır, donanım desteklemiyorsa daha yüksek çözünürlüklü bir akışı kırpıp küçültebilir. Bir yeteneği zorunlu kılmak için min, max veya exact anahtar kelimelerini kullanırsınız. Zorunlu kısıt karşılanamazsa söz OverconstrainedError ile reddedilir ve MDN'ye göre bu durumda kullanıcıya izin sorusu bile gösterilmez.
// Tercih: 1280x720'ye en yakın
{ video: { width: { ideal: 1280 }, height: { ideal: 720 } } }
// Zorunlu alt sınır ve aralık
{
video: {
width: { min: 1024, ideal: 1280, max: 1920 },
height: { min: 576, ideal: 720, max: 1080 },
},
}
// Ön kamera tercihi / arka kamera zorunluluğu (mobil)
{ video: { facingMode: "user" } }
{ video: { facingMode: { exact: "environment" } } }
// Düşük kare hızı (ör. dar bant genişliği)
{ video: { frameRate: { ideal: 10, max: 15 } } }| Yazım | Davranış | Karşılanamazsa |
|---|---|---|
| width: 1280 | İdeal tercih | En yakın değer seçilir |
| width: { ideal: 1280 } | İdeal tercih (yukarıdakiyle aynı) | En yakın değer seçilir |
| width: { min: 1280 } | Zorunlu alt sınır | OverconstrainedError |
| facingMode: { exact: "environment" } | Zorunlu arka kamera | OverconstrainedError |
| deviceId: { exact: id } | Zorunlu belirli cihaz | OverconstrainedError |
Tarayıcının iç bilgileri de isteğinizi geçersiz kılabilir. MDN'nin örneğinde, kullanıcı izin verirken başka bir kamera seçtiyse, sizin deviceId ile istediğiniz kamera yerine o kamera öncelik kazanabilir; belirli bir kamerayı şart koşmak için exact kullanmanız gerekir. Gerçekte hangi değerlerin seçildiğini öğrenmek için iz nesnesinin getSettings() yöntemi vardır: bu yöntem sitenin belirlediği değerleri değil, platform varsayılanları dahil o anki bütün ayarları döndürür.
Cihazları listelemek
navigator.mediaDevices.enumerateDevices() kullanılabilir giriş ve çıkış cihazlarını MediaDeviceInfo dizisi olarak döndürür. Güvenli bağlam gerektirir, belgenin tam etkin ve görünür olması gerekir. Gizlilik nedeniyle izin verilmeden önce cihaz adları (label) boş gelebilir; MDN'nin örnek çıktısında izin öncesinde yalnızca tür ve kimlik görünür, akış etkinken ya da kalıcı izin varken adlar da listelenir. Varsayılan cihazlar listenin başında yer alır.
const cihazlar = await navigator.mediaDevices.enumerateDevices();
cihazlar.forEach((cihaz) => {
console.log(`${cihaz.kind}: ${cihaz.label} id = ${cihaz.deviceId}`);
});Kamerayı kapatmak
Akışı artık kullanmıyorsanız izleri durdurmalısınız; aksi halde kamera ışığı ve tarayıcının kullanım göstergesi açık kalır. MediaStreamTrack.stop(), iz nesnesinin kaynağa artık ihtiyaç duymadığını bildirir ve readyState değerini hemen ended yapar. MDN iki ayrıntı verir: aynı kaynağı birden fazla iz kullanabildiği için kaynak hemen kapanmayabilir, son iz de bırakınca tamamen durur; ve stop() ile durdurulan iz için ended olayı tetiklenmez.
function kamerayiKapat(videoElem) {
const stream = videoElem.srcObject;
stream.getTracks().forEach((track) => track.stop());
videoElem.srcObject = null;
}MediaStream nesnesinin getTracks(), getVideoTracks() ve getAudioTracks() yöntemleri vardır; MDN, dönen listelerin sırasının tanımlı olmadığını ve tarayıcıdan tarayıcıya hatta çağrıdan çağrıya değişebileceğini belirtir. Mobilde ön ve arka kamera arasında geçiş yaparken MDN, yeni facingMode istemeden önce mevcut izi stop() ile bırakmayı önerir.
Hata adları ve anlamları

| Hata | Ne zaman |
|---|---|
| NotAllowedError | Kullanıcı veya tarayıcı erişimi reddetti, sayfa güvensiz bağlamda ya da Permissions Policy izin vermiyor |
| NotFoundError | İstenen türde ve kısıtlara uyan iz bulunamadı |
| NotReadableError | İzin verildi ama işletim sistemi, tarayıcı veya donanım düzeyinde cihaza erişim engellendi |
| OverconstrainedError | Zorunlu kısıtları karşılayan cihaz yok; constraint alanı karşılanamayan kısıtın adını verir |
| AbortError | İzin ve donanım sorunsuz ama başka bir sorun cihazın kullanılmasını engelledi |
| SecurityError | Belgede kullanıcı medyası desteği devre dışı |
| InvalidStateError | Belge tam etkin değil |
| TypeError | Kısıt listesi boş ya da hepsi false; güvensiz bağlamda mediaDevices undefined olduğu için de görülebilir |
Tablodaki adlar err.name değeridir; kullanıcıya gösterilecek mesajı bu ada göre seçebilirsiniz. MDN, OverconstrainedError için ilginç bir not düşer: bu hata kullanıcı henüz izin vermeden de oluşabildiği için parmak izi çıkarmada (fingerprinting) kullanılabilecek bir yüzeydir. Eski kodlarda gördüğünüz hata adlarına da dikkat edin: MDN'ye göre belirtimin eski sürümleri bugünkü NotAllowedError durumu için SecurityError kullanıyordu; SecurityError artık tabloda gösterilen farklı bir anlam taşır. Web API'lerindeki DOMException adlarının genel listesi ve güvenli bağlam ile kullanıcı etkinliği kuralları Web API hataları ve güvenli bağlam yazısında bir arada anlatılıyor.
Güvenli bağlam, izin ve Permissions Policy
getUserMedia() yalnızca güvenli bağlamlarda kullanılabilen güçlü bir özelliktir. MDN güvenli bağlamı kısaca HTTPS ile, file:/// şemasıyla ya da localhost üzerinden yüklenen sayfa olarak tanımlar. Canlı sitede HTTPS kurulumu HTTPS ve TLS rehberinde, HTTP kaynakların neden olduğu karışık içerik sorunu mixed content yazısında anlatılıyor. Bunun yanında kullanıcı izni her zaman gerekir; tarayıcılar alan adı başına kalıcı izin sunabilir ama en azından ilk seferde sormak zorundadır. Ayrıca kamera veya mikrofon kullanılırken bir gösterge göstermeleri gerekir.
İzin isteği yalnızca geçerli bir kökene sahip üst düzey belgeden yapılabilir. Bir <iframe> içinde çalışacaksanız üst belgenin Permissions Policy ile izin vermesi gerekir. İlgili yönergeler camera ve microphone adlarını taşır:
Permissions-Policy: camera=(self)
Permissions-Policy: microphone=(self "https://developer.mozilla.org")<iframe src="https://mycode.example.net/etc" allow="camera; microphone"></iframe>MDN, kökeni olmayan bazı durumlarda getUserMedia() çağrısına izin verilmediğini de sayar: sandbox özniteliği allow-same-origin içermeyen bir iframe, kullanıcının adres çubuğuna yazdığı bir data:// veya blob:// adresi, srcdoc ile içeriği verilen bir çerçeve gibi. İzin durumunu önceden öğrenmek için navigator.permissions.query() kullanılabilir; MDN'ye göre sorgulanabilecek adlar arasında camera ve microphone vardır, desteklenmeyen bir ad verilirse söz TypeError ile reddedilir.
Gizlilik göstergeleri
Belirtim, tarayıcıların kamera ve mikrofon kullanımı konusunda kullanıcıyı açıkça bilgilendirmesini şart koşar. MDN'ye göre tarayıcılar, cihazın kendi donanım ışığına ek olarak bir kamera veya mikrofonun kullanımda olduğunu gösteren bir gösterge sunmak zorundadır; izin verilmiş ama o an kayıt yapılmıyorsa bile izin verildiğini gösteren bir işaret bulunmalıdır. MDN örnek olarak Firefox'un adres çubuğunda kayıt sırasında titreşen kırmızı, izin olup kayıt yokken gri bir simge gösterdiğini anlatır. Bu göstergeler sizin kontrolünüzde değildir; sizin tarafınızda kullanıcıya güven vermenin yolu, izni yalnızca gerektiğinde istemek ve işiniz bitince izleri durdurmaktır.
Kameradan fotoğraf almak
MDN'nin fotoğraf eğitimi, video elemanını canvas'a çizerek anlık görüntü alır. Video oynamaya başlayınca canplay olayıyla boyutlar ayarlanır; düğmeye basılınca o anki kare drawImage() ile canvas'a çizilir ve toDataURL("image/png") ile bir <img> elemanına verilir. MDN, HTMLVideoElement nesnesinin resim kabul eden her API'ye resim gibi davrandığını ve o anki karenin resim içeriği olarak kullanıldığını belirtir:
function fotografCek(video, canvas, photo, width, height) {
const context = canvas.getContext("2d");
if (width && height) {
canvas.width = width;
canvas.height = height;
context.drawImage(video, 0, 0, width, height);
photo.setAttribute("src", canvas.toDataURL("image/png"));
}
}Canvas'a çizme ve dışa aktarma ayrıntıları ile büyük görüntülerde neden toBlob() tercih edildiği Canvas görsel ve piksel işlemleri yazısında anlatılıyor.
MediaRecorder ile kayıt
Akışı kaydetmek için MediaStream Recording API'nin MediaRecorder arayüzü kullanılır. new MediaRecorder(stream, options) kaydediciyi oluşturur, start() kaydı başlatır, kayıt verisi dataavailable olaylarıyla parça parça gelir, stop() kaydı bitirir ve son dataavailable ardından stop olayı tetiklenir. Parçaları birleştirip bir Blob oluşturursunuz:
const stream = await navigator.mediaDevices.getUserMedia({ audio: true });
const tur = MediaRecorder.isTypeSupported("audio/webm;codecs=opus")
? "audio/webm;codecs=opus"
: "";
const recorder = new MediaRecorder(stream, tur ? { mimeType: tur } : {});
let parcalar = [];
recorder.ondataavailable = (e) => {
parcalar.push(e.data);
};
recorder.onstop = () => {
const blob = new Blob(parcalar, { type: recorder.mimeType });
parcalar = [];
const audio = document.createElement("audio");
audio.controls = true;
audio.src = URL.createObjectURL(blob);
document.body.append(audio);
};
kaydetDugmesi.onclick = () => recorder.start();
durdurDugmesi.onclick = () => recorder.stop();state özelliği inactive, recording veya paused değerlerinden birini taşır; MDN'nin örneğinde start() sonrasında recording yazdırılır. pause() ve resume() kaydı duraklatıp sürdürür, requestData() o ana kadarki veriyi bir Blob olarak ister ve kayıt yeni bir Blob'a devam eder.
timeslice, biçim ve hatalar
start(timeslice) ile milisaniye cinsinden bir dilim verirseniz kayıt bu sürelik parçalar halinde gelir; vermezseniz bütün kayıt tek bir Blob'a yazılır. MDN, diğer web API zaman değerleri gibi timeslice değerinin de kesin olmadığını, gerçek aralıkların biraz daha uzun olabileceğini belirtir. Kayıt biçimini mimeType seçeneğiyle istersiniz; desteklenmeyen bir tür yapıcıda NotSupportedError fırlatır, bu yüzden önce MediaRecorder.isTypeSupported() ile kontrol etmek iyi bir alışkanlıktır. Çözünürlük ve kare hızı kaydedicide değil, getUserMedia() kısıtlarında belirtilir. Kodlama hızını ise yapıcı seçenekleriyle verirsiniz: audioBitsPerSecond, videoBitsPerSecond ya da ikisinin yerine geçebilen bitsPerSecond. MDN, video için değer verilmezse tarayıcıya göre 2.5 Mbps veya 10 Mbps varsayıldığını, ses için varsayılanın örnekleme hızına ve kanal sayısına göre uyarlandığını belirtir. Gerçekte kullanılan değerler kaydedicinin aynı adlı özelliklerinden okunur ve istediğinizden farklı olabilir.
start() çağrısı kaydedici zaten inactive durumda değilse InvalidStateError, akış etkin değilse veya bir iz kaydedilemeyen biçimdeyse NotSupportedError, akış kayda izin vermiyorsa SecurityError fırlatır. Kayıt sırasında oluşan ölümcül hatalar error olayıyla bildirilir; ardından o ana kadar toplanan verilerle bir dataavailable ve son olarak stop olayı gelir. Akış kendiliğinden biterse, örneğin kullanıcı mikrofon paylaşımını durdurursa, kayıt da durur. Kaydı dosya olarak indirmek veya sunucuya göndermek için File API ve Blob yazısına bakın.
Ekran paylaşımı: getDisplayMedia()
Kamera yerine ekranın ya da bir pencerenin görüntüsünü almak için navigator.mediaDevices.getDisplayMedia() kullanılır. Kullanıcıdan paylaşılacak yüzeyi seçmesini ister ve sonucu yine bir MediaStream olarak verir; bu akış MediaRecorder ile kaydedilebilir. MDN'ye göre bu çağrı geçici kullanıcı etkinliği (transient user activation) gerektirir, yani bir tıklama gibi bir olay işleyicisinden yapılmalıdır; aksi halde hata alırsınız.
Sık yapılan hatalar

- Sayfayı HTTP üzerinden test etmek.
navigator.mediaDevicesundefinedolur; HTTPS veyalocalhostkullanın. - Boş kısıt vermek.
getUserMedia({})her zaman reddedilir; en az bir türütrueya da nesne yapın. - Zorunlu kısıtları gereksiz kullanmak.
min/exactkarşılanamazsa izin bile sorulmadanOverconstrainedErroralırsınız; çoğu durumdaidealyeterlidir. - İzleri durdurmamak. Kamera ışığı açık kalır; iş bitince her izde
stop()çağırın. - iframe içinde izin vermeden çağırmak. Üst belgede
allow="camera; microphone"veya Permissions Policy gerekir. - MediaRecorder için biçimi kontrol etmemek. Desteklenmeyen
mimeTypeNotSupportedErrorverir;isTypeSupported()ile seçin.
Sık Sorulan Sorular
Kullanıcı izni reddettikten sonra tekrar sorabilir miyim?
Reddetme durumunda söz NotAllowedError ile reddedilir. MDN, kullanıcının o oturum için ya da genel olarak erişimi reddetmiş olabileceğini belirtir; böyle bir durumda kullanıcıya izni tarayıcı ayarlarından nasıl açabileceğini anlatan bir mesaj göstermek daha doğru olur. Durumu navigator.permissions.query({ name: "camera" }) ile önceden kontrol edebilirsiniz.
Yerel geliştirmede HTTPS şart mı?
Hayır. MDN'nin tanımına göre localhost üzerinden yüklenen sayfalar da güvenli bağlam sayılır. Canlı sunucuda ise HTTPS gerekir; HTTP üzerinden açılan bir sayfada navigator.mediaDevices tanımsız olduğu için kodunuz izin sorusuna bile ulaşamaz.
Kaydı hangi biçimde alırım?
Biçim tarayıcıya bağlıdır. MediaRecorder.isTypeSupported() ile video/webm, video/mp4 gibi türleri test edin ve desteklenen ilkini seçin. Seçilen biçim kaydedicinin mimeType özelliğinden okunur.
Canvas animasyonunu video olarak kaydedebilir miyim?
Evet. MDN'ye göre MediaRecorder yapıcısına verilen akış getUserMedia() dışında bir <canvas>, <audio> veya <video> elemanından da gelebilir; canvas için HTMLCanvasElement.captureStream() bir MediaStream döndürür.
Marka adları sahiplerine aittir. Bu yazı bağımsız bir teknik rehberdir; adı geçen kuruluşlarla bayilik veya ortaklık ilişkisi bulunduğu anlamına gelmez.
İlgili içerikler: Web API öğrenme rehberi · Web API hataları ve güvenli bağlam · Canvas görsel ve piksel işlemleri · File API ve Blob · HTTPS ve TLS rehberi · Mixed content hatası