main
video#myFace(autoplay,playsinline, width="400", height="400")
//MeadiaDivces.getUserMedia 예시
async function getMedia(constraints) {
let stream = null;
try {
stream = await navigator.mediaDevices.getUserMedia(constraints);
/* 스트림 사용 */
} catch(err) {
/* 오류 처리 */
}
}
-> srcObject: 인터페이스 의 srcObject속성은 에 HTMLMediaElement연결된
미디어의 소스 역할을 하는 개체를 설정하거나 반환합니다.
//srcObject 예시
const mediaStream = await navigator.mediaDevices.getUserMedia({video: true});
const video = document.createElement('video');
video.srcObject = mediaStream;
-> app.js코드
const myFace = document.getElementById("myFace");
let myStream;
async function getMedia(){
try{
myStream = await navigator.mediaDevices.getUserMedia({
audio: true,
video: true,
});//user의 media를 가져옴(우린 특정 값을 줘서 카메라와 오디오를 가져옴)
myFace.srcObject = myStream;//비디오를 Html에 넣어서 화면에 보이도록 함.
} catch(error){
console.log(error)
}
};
getMedia();
main
div#myStream
video#myFace(autoplay,playsinline, width="400", height="400")
button#mute Mute
button#camera Turn Camera Off
//밑의 코드들 추가해주기
const muteBtn = document.getElementById("mute");
const cameraBtn = document.getElementById("camera");
let muted = false;
let cameraOff = false;
function handleMuteClick(){
//console.log(myStream.getAudioTracks()); -> track정보(inspect) 제공
myStream.getAudioTracks().forEach((track) => (track.enabled = !track.enabled));
if(!muted){//muted = true라면 - default와 반대되는 값이라면// !: 반대되는 값을 리턴해줌
muteBtn.innerText = "Unmute";
muted = true;
} else {
muteBtn.innerText = "Mute";
muted = false;
}
}
function handleCameraClick(){
//console.log(myStream.getVideoTracks()); -> track정보(inspect) 제공
myStream.getVideoTracks().forEach((track) => (track.enabled = !track.enabled));
if(cameraOff){//cameraOff가 참일 때
cameraBtn.innerText = "Turn Camera Off";
cameraOff = false;
} else{
cameraBtn.innerText = "Turn Camera On";
cameraOff = true;
}
}
muteBtn.addEventListener("click", handleMuteClick);
cameraBtn.addEventListener("click", handleCameraClick);
-> home.pug
-> select: 선택창 / option: select창의 내용물

//추가
select#cameras
//최종 코드
main
div#myStream
video#myFace(autoplay,playsinline, width="400", height="400")
button#mute Mute
button#camera Turn Camera Off
select#cameras
<!--내용물: option(value="device") Face Camera 이게 내용물이 될 것임-->
-> app.js
~~ MediaDevices.eumerateDevices MDN MediaDevices의 enumerateDevices() 메서드는 사용(또는 접근)이 가능한 미디어 입력장치나 출력장치들의 리스트를 가져옵니다. 예를 들면 마이크, 카메라, 헤드셋 등의 미디어 입/출력 장치 리스트를 불러오는 것 이죠. 이 메서드는 Promise (en-US)를 반환하는데, 이 Promise가 resolve되면 장치(device)정보가 들어있는 MediaDeviceInfo (en-US) 배열(array)을 확인할 수 있습니다. ~~
filter((객체) => 조건문): array에다가 실행하는 함수임. array의 객체중 조건문에 해당하는 즉, 조건문에 true를 반환하는 애들만 따로 모아서 새 array를 만듦
? : 조건부 연산자.
condition ? value1 : value2;
//condition이 truthy라면 value1이, 그렇지 않으면 value2가 반환됩니다.
const camerasSelect = document.getElementById("cameras");
async function getCameras(){
try{
const devices = await navigator.mediaDevices.enumerateDevices();
//console.log(devices); -> 확인해보삼
const cameras = devices.filter((device) => device.kind === "videoinput");//true인 애들만 뽑아서 새 array만듦.
//console.log(cameras); -> videoinput만 있는 새 array 만들어짐.
const currentCamera = myStream.getVideoTracks()[0];//현재 사용중인 카메라 정보
cameras.forEach((camera) => {
const option = document.createElement("option");
option.value = camera.deviceId;
option.innerText = camera.label;
if(currentCamera.label === camera.label){
option.selected = true;
};//현재 카메라 정보가 카메라 목록에 있는 것과 이름이 같다면, 그 option이 select되게 해라. -> 처음 시작할 때, 현재 카메라가 선택되도록 하고 싶어서 그런 것임.
camerasSelect.appendChild(option);//카메라 장치 목록을 select창에 넣음.
})
} catch(error){
console.log(error);
}
}
async function getMedia(deviceId){
const initialConstrains = {
audio: true,
video: { facingMode : "user" },
};
const cameraConstrains = {
audio: true,
video: { deviceId: { exact: deviceId } },//우리가 임의로 비디오를 바꿀 수 있도록 constrain을 설정해주는 것임.
}
try{
myStream = await navigator.mediaDevices.getUserMedia(
deviceId? cameraConstrains : initialConstrains
);//user의 media를 가져옴(이 때 특정 값은 ?(조건부 연산자)를 이용해 정해주고 특정 media객체를 가져옴)
myFace.srcObject = myStream;
if(!deviceId){
await getCameras();
}//내 장치에 있는 카메라들 가져오기! -> if문으로 계속 실행되는거 막기
} catch(error){
console.log(error)
}
};
async function handleCameraChange(){
await getMedia(camerasSelect.value);//select입력 값을 media시작 함수에 넣어주고 시작함수에서는 이를 받아들여, 카메라를 사용함.
}
camerasSelect.addEventListener("input", handleCameraChange);//select의 이벤트는 input임
-> 결과 코드
doctype html
html(lang="en")
head
meta(charset="UTF-8")
meta(http-equiv="X-UA-Compatible", content="IE=edge")
meta(name="viewport", content="width=device-width, initial-scale=1.0")
link(rel="stylesheet", href="https://unpkg.com/mvp.css")
title Noom
body
header
h1 Noom
main
div#myStream
video#myFace(autoplay,playsinline, width="400", height="400")
button#mute Mute
button#camera Turn Camera Off
select#cameras
script(src="/socket.io/socket.io.js")
script(src="/public/js/app.js")
const frontSocket = io();//backend Socket과 연결됨.
const myFace = document.getElementById("myFace");
const muteBtn = document.getElementById("mute");
const cameraBtn = document.getElementById("camera");
const camerasSelect = document.getElementById("cameras");
let myStream;
let muted = false;
let cameraOff = false;
async function getCameras(){
try{
const devices = await navigator.mediaDevices.enumerateDevices();
//console.log(devices); -> 확인해보삼
const cameras = devices.filter((device) => device.kind === "videoinput");//true인 애들만 뽑아서 새 array만듦.
//console.log(cameras); -> videoinput만 있는 새 array 만들어짐.
const currentCamera = myStream.getVideoTracks()[0];
cameras.forEach((camera) => {
const option = document.createElement("option");
option.value = camera.deviceId;
option.innerText = camera.label;
if(currentCamera.label === camera.label){
option.selected = true;
}
camerasSelect.appendChild(option);
})
} catch(error){
console.log(error);
}
}
async function getMedia(deviceId){
const initialConstrains = {
audio: true,
video: { facingMode : "user" },
};
const cameraConstrains = {
audio: true,
video: { deviceId: { exact: deviceId } },
}
try{
myStream = await navigator.mediaDevices.getUserMedia(
deviceId? cameraConstrains : initialConstrains
);//user의 media를 가져옴(우린 특정 값을 줘서 카메라와 오디오를 가져옴)
myFace.srcObject = myStream;
if(!deviceId){
await getCameras();
}// deviceId가 없다면 카메라 목록을 가져와라! -> if문 없이 하면 카메라 바꿀 때마다 이 gerMedia함수가 계속 실행되니까 목록이 늘어남
} catch(error){
console.log(error)
}
};
getMedia();
function handleMuteClick(){
//console.log(myStream.getAudioTracks()); -> track정보(inspect) 제공
myStream.getAudioTracks().forEach((track) => (track.enabled = !track.enabled));
if(!muted){//muted = true라면 - default와 반대되는 값이라면// !: 반대되는 값을 리턴해줌
muteBtn.innerText = "Unmute";
muted = true;
} else {
muteBtn.innerText = "Mute";
muted = false;
}
}
function handleCameraClick(){
//console.log(myStream.getVideoTracks()); -> track정보(inspect) 제공
myStream.getVideoTracks().forEach((track) => (track.enabled = !track.enabled));
if(cameraOff){//cameraOff가 참일 때
cameraBtn.innerText = "Turn Camera Off";
cameraOff = false;
} else{
cameraBtn.innerText = "Turn Camera On";
cameraOff = true;
}
}
async function handleCameraChange(){
await getMedia(camerasSelect.value);
}
muteBtn.addEventListener("click", handleMuteClick);
cameraBtn.addEventListener("click", handleCameraClick);
camerasSelect.addEventListener("input", handleCameraChange);
-> 다음 강의에선 상대방 비디오 가져오기 등등 할 것임.