บทนำ
การบันทึกเสียง/วิดีโอเป็น "จอกศักดิ์สิทธิ์" ของการพัฒนาเว็บมาเป็นเวลานาน เป็นเวลาหลายปีที่เราต้องพึ่งพาปลั๊กอินเบราว์เซอร์ (Flash หรือ Silverlight) เพื่อทำงานให้เสร็จ เร็วเข้า
HTML5 ช่วยคุณได้ แม้ว่าอาจจะไม่ชัดเจน แต่การเพิ่มขึ้นของ HTML5 ทำให้การเข้าถึงฮาร์ดแวร์ของอุปกรณ์เพิ่มขึ้นอย่างรวดเร็ว ตำแหน่งทางภูมิศาสตร์ (GPS), Orientation API (ตัวตรวจวัดความเร่ง), WebGL (GPU) และ Web Audio API (ฮาร์ดแวร์เสียง) เป็นตัวอย่างที่สมบูรณ์แบบ ฟีเจอร์เหล่านี้มีประสิทธิภาพสูงมาก โดยแสดง JavaScript API ระดับสูงที่อยู่เหนือความสามารถของฮาร์ดแวร์พื้นฐานของระบบ
บทแนะนำนี้จะแนะนำ API ใหม่ GetUserMedia ซึ่งช่วยให้เว็บแอปเข้าถึงกล้องและไมโครโฟนของผู้ใช้ได้
เส้นทางสู่ getUserMedia()
หากคุณไม่ทราบประวัติความเป็นมา วิธีที่เราได้มาซึ่ง API getUserMedia() ถือเป็นเรื่องราวที่น่าสนใจ
"Media Capture API" หลายรูปแบบได้รับการพัฒนาในช่วงไม่กี่ปีที่ผ่านมา หลายคนตระหนักถึงความจำเป็นในการเข้าถึงอุปกรณ์ที่มาพร้อมเครื่องบนเว็บ แต่ก็ทำให้ทุกคนและแม่ของพวกเขาต้องร่วมกันสร้างข้อกำหนดใหม่ สิ่งต่างๆ ยุ่งเหยิงมากจนในที่สุด W3C ก็ตัดสินใจจัดตั้งคณะทำงานขึ้นมา จุดประสงค์เดียวของพวกเขาคืออะไร ทำความเข้าใจความบ้าคลั่งนี้ กลุ่มทำงานด้านนโยบาย API ของอุปกรณ์ (DAP) ได้รับมอบหมายให้รวบรวมและกำหนดมาตรฐานข้อเสนอจำนวนมาก
เราจะพยายามสรุปสิ่งที่เกิดขึ้นในปี 2011…
รอบที่ 1: HTML Media Capture
HTML Media Capture เป็นความพยายามครั้งแรกของ DAP ในการกำหนดมาตรฐานการบันทึกสื่อบนเว็บ โดยจะทำงานด้วยการโอเวอร์โหลด <input type="file"> และเพิ่มค่าใหม่สำหรับพารามิเตอร์ accept
หากต้องการให้ผู้ใช้ถ่ายภาพตนเองด้วยเว็บแคม
คุณสามารถทำได้ด้วย capture=camera โดยทำดังนี้
<input type="file" accept="image/*;capture=camera">
การบันทึกวิดีโอหรือเสียงมีลักษณะคล้ายกัน ดังนี้
<input type="file" accept="video/*;capture=camcorder">
<input type="file" accept="audio/*;capture=microphone">
ดีใช่ไหมล่ะ ฉันชอบที่เครื่องมือนี้ใช้ไฟล์อินพุตซ้ำได้ ในทางความหมาย
มันสมเหตุสมผลมาก สิ่งที่ "API" นี้ยังทำไม่ได้คือความสามารถในการสร้างเอฟเฟกต์แบบเรียลไทม์ (เช่น แสดงผลข้อมูลเว็บแคมแบบสดไปยัง <canvas> และใช้ตัวกรอง WebGL)
HTML Media Capture ช่วยให้คุณบันทึกไฟล์สื่อหรือถ่ายภาพสแนปชอตได้ในขณะนั้นเท่านั้น
การสนับสนุน:
- เบราว์เซอร์ Android 3.0 - หนึ่งในการใช้งานครั้งแรกๆ ดูวิดีโอนี้เพื่อดูการใช้งานจริง
- Chrome สำหรับ Android (0.16)
- Firefox Mobile 10.0
- iOS6 Safari และ Chrome (รองรับบางส่วน)
รอบที่ 2: องค์ประกอบของอุปกรณ์
หลายคนคิดว่า HTML Media Capture มีข้อจำกัดมากเกินไป จึงเกิดข้อกำหนดใหม่ที่รองรับอุปกรณ์ (ในอนาคต) ทุกประเภท จึงไม่น่าแปลกใจที่การออกแบบนี้
ต้องใช้องค์ประกอบใหม่ นั่นคือองค์ประกอบ <device>
ซึ่งกลายเป็นต้นแบบของ getUserMedia()
Opera เป็นหนึ่งในเบราว์เซอร์แรกๆ ที่สร้างการใช้งานครั้งแรกของการจับภาพวิดีโอโดยอิงตามองค์ประกอบ <device> หลังจากนั้นไม่นาน (ในวันเดียวกัน) WhatWG ตัดสินใจยกเลิกแท็ก <device> เพื่อหันไปใช้แท็กใหม่ที่กำลังมาแรง ซึ่งครั้งนี้เป็น JavaScript API ที่ชื่อว่า navigator.getUserMedia() หนึ่งสัปดาห์ต่อมา Opera ได้เปิดตัวบิลด์ใหม่ที่รองรับข้อกำหนด getUserMedia() ที่อัปเดตแล้ว และในปีเดียวกันนั้น Microsoft ก็ได้เข้าร่วมด้วยการเปิดตัว Lab สำหรับ IE9 ที่รองรับข้อกำหนดใหม่
<device> จะมีลักษณะดังนี้
<device type="media" onchange="update(this.data)"></device>
<video autoplay></video>
<script>
function update(stream) {
document.querySelector('video').src = stream.url;
}
</script>
การสนับสนุน:
แต่เบราว์เซอร์ที่เปิดตัวแล้วไม่มี <device>
ก็ถือว่ามี API ให้กังวลน้อยลงไปอีกหนึ่งตัว :) อย่างไรก็ตาม <device> ก็มีข้อดีอยู่ 2 อย่างคือ 1.) เป็นเชิงความหมาย และ 2.) ขยายได้ง่ายเพื่อรองรับมากกว่าแค่อุปกรณ์เสียง/วิดีโอ
พักหายใจสักหน่อย ทุกอย่างจะเกิดขึ้นเร็วมาก
รอบที่ 3: WebRTC
องค์ประกอบ <device> จึงหายไปในที่สุด
การค้นหา API การจับภาพที่เหมาะสมนั้นรวดเร็วขึ้นเนื่องจากความพยายามที่มากขึ้นของ WebRTC (Web Real Time Communications) ข้อกำหนดเฉพาะดังกล่าวอยู่ภายใต้การดูแลของคณะทำงาน WebRTC ของ W3C Google, Opera, Mozilla และอื่นๆ อีก 2-3 รายมีการใช้งานแล้ว
getUserMedia() เกี่ยวข้องกับ WebRTC เนื่องจากเป็นเกตเวย์ไปยังชุด API ดังกล่าว
ซึ่งเป็นวิธีเข้าถึงสตรีมกล้อง/ไมโครโฟนในเครื่องของผู้ใช้
การสนับสนุน:
getUserMedia() ได้รับการรองรับตั้งแต่ Chrome 21, Opera 18 และ Firefox 17
เริ่มต้นใช้งาน
navigator.mediaDevices.getUserMedia() ช่วยให้เราสามารถใช้ประโยชน์จากอินพุตของเว็บแคมและไมโครโฟนได้โดยไม่ต้องใช้ปลั๊กอิน
ตอนนี้คุณสามารถเข้าถึงกล้องได้ทันทีโดยไม่ต้องติดตั้งแอป ซึ่งติดตั้งมาในเบราว์เซอร์โดยตรง ตื่นเต้นไหม
การตรวจจับฟีเจอร์
การตรวจหาฟีเจอร์เป็นการตรวจสอบง่ายๆ ว่ามี navigator.mediaDevices.getUserMedia หรือไม่ ดังนี้
if (navigator.mediaDevices?.getUserMedia) {
// Good to go!
} else {
alert("navigator.mediaDevices.getUserMedia() is not supported");
}
การเข้าถึงอุปกรณ์อินพุต
เราต้องขอสิทธิ์เพื่อใช้เว็บแคมหรือไมโครโฟน
พารามิเตอร์แรกของ navigator.mediaDevices.getUserMedia() คือออบเจ็กต์ที่ระบุรายละเอียดและข้อกำหนดสำหรับสื่อแต่ละประเภทที่คุณต้องการเข้าถึง ตัวอย่างเช่น หากต้องการเข้าถึงเว็บแคม พารามิเตอร์แรกควรเป็น {video: true} หากต้องการใช้ทั้งไมโครโฟนและกล้อง ให้ส่ง {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>
โอเค เกิดอะไรขึ้นที่นี่ การจับภาพสื่อเป็นตัวอย่างที่สมบูรณ์แบบของ API ใหม่ๆ ของ HTML5 ที่ทำงานร่วมกัน โดยจะทำงานร่วมกับเพื่อน HTML5 อื่นๆ ของเราอย่าง <audio> และ <video>
โปรดสังเกตว่าเราไม่ได้ตั้งค่าแอตทริบิวต์ src หรือรวมองค์ประกอบ <source> ไว้ในองค์ประกอบ <video> แทนที่จะป้อน URL ของไฟล์สื่อให้กับวิดีโอ เราจะตั้งค่า srcObject เป็นออบเจ็กต์ LocalMediaStream ที่แสดงถึงเว็บแคม
ฉันยังบอกให้ <video> autoplay ด้วย ไม่เช่นนั้นวิดีโอจะค้างอยู่ที่เฟรมแรก การเพิ่ม controls ก็ทำงานได้ตามที่คาดไว้เช่นกัน
การตั้งค่าข้อจำกัดของสื่อ (ความละเอียด ความสูง ความกว้าง)
พารามิเตอร์แรกของ getUserMedia() ยังใช้เพื่อระบุข้อกำหนด (หรือข้อจำกัด) เพิ่มเติมในสตรีมสื่อที่แสดงผลได้ด้วย ตัวอย่างเช่น แทนที่จะระบุเพียงว่าคุณต้องการสิทธิ์เข้าถึงวิดีโอขั้นพื้นฐาน (เช่น {video: true}) คุณสามารถกำหนดให้สตรีมเป็นแบบ 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);
ดูการกำหนดค่าเพิ่มเติมได้ที่ API ข้อจำกัด
การเลือกแหล่งที่มาของสื่อ
เมธอด enumerateDevices() ของอินเทอร์เฟซ MediaDevices จะขอรายการอุปกรณ์อินพุตและอุปกรณ์เอาต์พุตสื่อที่พร้อมใช้งาน เช่น ไมโครโฟน กล้อง ชุดหูฟัง และอื่นๆ Promise ที่แสดงผลจะได้รับการแก้ไขด้วยอาร์เรย์ของออบเจ็กต์ MediaDeviceInfo ที่อธิบายอุปกรณ์
ในตัวอย่างนี้ ไมโครโฟนและกล้องล่าสุดที่พบจะได้รับเลือกเป็นแหล่งที่มาของสตรีมสื่อ
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);
}
ดูการสาธิตที่ยอดเยี่ยมของ Sam Dutton เกี่ยวกับวิธีให้ผู้ใช้เลือกแหล่งที่มาของสื่อ
ความปลอดภัย
เบราว์เซอร์จะแสดงกล่องโต้ตอบสิทธิ์เมื่อเรียกใช้ navigator.mediaDevices.getUserMedia() ซึ่งจะให้ตัวเลือกแก่ผู้ใช้ในการให้หรือปฏิเสธสิทธิ์เข้าถึงกล้อง/ไมโครโฟน ตัวอย่างเช่น นี่คือกล่องโต้ตอบสิทธิ์ของ Chrome
การระบุรายการสำรอง
สำหรับผู้ใช้ที่ไม่มีการรองรับ navigator.mediaDevices.getUserMedia() ตัวเลือกหนึ่งคือการเปลี่ยนไปใช้ไฟล์วิดีโอที่มีอยู่หากระบบไม่รองรับ API และ/หรือการเรียกใช้ไม่สำเร็จด้วยเหตุผลบางประการ
if (!navigator.mediaDevices?.getUserMedia) {
video.src = "fallbackvideo.webm";
} else {
const stream = await navigator.mediaDevices.getUserMedia({ video: true });
video.srcObject = stream;
}