<form class="login">
  <input type="text" id="userNick" placeholder="닉네임을 입력하세요!" required />
  <button type="submit">등록</button>
</form>
<div class="cam">
  <div class="cam_wrap">
    <div class="my_cam"></div>
    <div class="remote_cam_wrap"></div>
  </div>
  <div class="cam-footer">
    <ul class="cam-btn">
      <li class="mic btn_on" onclick="mic_on_off(this)">
        <img src="https://www.vchatcloud.com/chat-demo/iframe/iframe_rtc_2/img/webRTC/on_mic.png" alt="마이크 재생" />
      </li>
      <li class="exit btn_on">
        <img src="https://www.vchatcloud.com/chat-demo/iframe/iframe_rtc_2/img/webRTC/exit.png" alt="화상채팅 종료" />
      </li>
      <li class="cam btn_on" onclick="cam_on_off(this)">
        <img src="https://www.vchatcloud.com/chat-demo/iframe/iframe_rtc_2/img/webRTC/on_cam.png" alt="카메라 재생" />
      </li>
    </ul>
  </div>
</div>
.login {
  margin-top: 100px;
  text-align: center;
}
.login input,
.login button {
  padding: 10px;
}

.cam {
  position: relative;
  text-align: center;
}

.cam_wrap {
  min-width: 880px;
  display: grid;
  grid-template-columns: 640px 240px;
  justify-content: center;
}
.remote_cam_wrap {
  display: flex;
  flex-direction: column;
  overflow: scroll;
  height: 360px;
}

.my_cam {
  position: relative;
  background: #999;
  height: 360px;
  align-content: center;
}
.remote_cam {
  position: relative;
  display: flex;
  flex-direction: column;
  background: #777;
  height: 135px;
}
.my_cam video,
.remote_cam video {
  width: 100%;
  height: 100%;
}
.my_cam p,
.remote_cam p {
  position: absolute;
  bottom: 15px;
  left: 15px;
  color: white;
}
.my_cam .camera_off {
  margin: 0 auto;
  margin-top: 130px;
}
.remote_cam .camera_off {
  width: 45px;
  height: 45px;
  margin: 0 auto;
  margin-top: 45px;
}

.my_cam .mic_off {
  position: absolute;
  top: 20px;
  right: 20px;
}
.remote_cam .mic_off {
  position: absolute;
  top: 10px;
  right: 10px;
}

.cam-footer {
  min-width: 880px;
  position: absolute;
  width: 100%;
  bottom: 5px;
}

ul.cam-btn {
  display: flex;
  justify-content: center;
}
.btn_on {
  margin: 5px;
  border: 2px solid #dedede;
  border-radius: 25px;
  background: white;
}
.btn_off {
  margin: 5px;
  border: 2px solid #dedede;
  border-radius: 25px;
  background: red;
}
// 채널에 접속하기 위해서 사용자의 userNick, userKey와 CMS에서 발급받은 channelKey가 필요합니다.
var channelKey = "YUcxwPRjTX-oVdUQqfLEE-20231212121518"; // CMS에서 발급받은 키 값, 발급 받은 키 값을 입력해보세요!

function videoInit() {
  // 로컬 마이크 상태 변경 이벤트
  channel.on('rtcLocalAudioChanged', function (event) {
    let is_mic = event.enable;
    $('.mic_off').css('display', !is_mic ? 'block' : 'none');
    $('.cam-footer .cam-btn .mic')
      .toggleClass('btn_on', is_mic)
      .toggleClass('btn_off', !is_mic);
    $('.cam-footer .cam-btn .mic img').attr('src', is_mic ? 'https://www.vchatcloud.com/chat-demo/iframe/iframe_rtc_2/img/webRTC/on_mic.png' : 'https://www.vchatcloud.com/chat-demo/iframe/iframe_rtc_2/img/webRTC/off_mic.png');
  });
  // 로컬 유저 비디오 소스 변경 이벤트
  channel.on('rtcLocalVideoChanged', function (event) {
    let is_cam = event.enable; // 로컬 접속자의 카메라 상태 체크
    $('.my_cam video').css('display', is_cam ? '' : 'none'); // 로컬 접속자의 카메라가 비활성화이면 video태그 숨김
    $('.my_cam .camera_off').css('display', !is_cam ? 'block' : 'none'); // 로컬 접속자의 카메라가 비활성화이면 대체 이미지 표시
    // 카메라 on/off버튼 상태를 재설정한다.
    $('.cam-footer .cam-btn .cam')
      .toggleClass('btn_on', is_cam)
      .toggleClass('btn_off', !is_cam);
    // is_cam 상태에 따라 on_cam.png를 보여줄 지 off_cam.png를 보여줄 지 설정한다.
    $('.cam-footer .cam-btn .cam img').attr('src', is_cam ? 'https://www.vchatcloud.com/chat-demo/iframe/iframe_rtc_2/img/webRTC/on_cam.png' : 'https://www.vchatcloud.com/chat-demo/iframe/iframe_rtc_2/img/webRTC/off_cam.png');
  });
  // 리모트 유저 접속 종료 이벤트
  channel.on("rtcRemoteStreamRemove", function (event) {
    console.log("rtcRemoteStreamRemove", event);
    let html = $(`.remote_cam_wrap div[name=${event.clientKey}]`); // 리모트 접속자를 표시하는 video태그를 감싸고 있는 태그
    if (html.length) {
      html.remove(); // 태그를 삭제한다 (비활성화)
    }
  });
  // 리모트 유저 접속 이벤트
  channel.on("rtcRemoteStreamAppend", function (event) {
    let stream = event.target;
    let html = $(`<div>`, {
      name: event.clientKey,
      class: "remote_cam"
    }); // 이벤트에서 받은 접속 단말 설정 고유키 데이터를 이용하여 새 div 생성
    remoteCamList.append(html); // 감싸고 있는 태그에 html소스를 삽입
    let remoteVideo = document.createElement("video");
    remoteVideo.srcObject = stream; // HTMLVideoElement에 미디어소스 설정
    remoteVideo.setAttribute("autoplay", true); // 자동 재생
    $(html).append(remoteVideo);
    let userNick = $("<p>").text(event.client.nickName);
    $(html).append(userNick); // 리모트 접속자의 닉네임 설정
    $(".nocam").toggleClass("active", stream.getVideoTracks().length == 0); // 카메라의 상태에 따라 화면 변경
    $(".nomic").toggleClass("active", stream.getAudioTracks().length == 0); // 마이크의 상태에 따라 화면 변경
    channel.setRTCRemoteMedia(remoteVideo, event.clientKey); // 채널에 리모트 미디어 설정
  });
  // 로컬 접속 종료 시
  channel.on("rtcLocalStreamRemove", function (event) {
    let html = $(".my_cam"); // 로컬 접속자를 표시하는 video태그를 감싸고 있는 태그
    html.html(''); // 태그를 삭제한다(움직이지 않는 비디오 태그 삭제)
  });
  // 로컬 접속 시
  channel.on("rtcLocalStreamAppend", function (event) {
    myVideo = document.createElement("video");
    let stream = event.target;
    myVideo.srcObject = stream;
    myVideo.setAttribute("autoplay", true);
    myCam.append(myVideo);
    let noCamera = document.createElement('img');
    noCamera.setAttribute('class', 'camera_off');
    noCamera.src = `https://www.vchatcloud.com/chat-demo/iframe/iframe_rtc_1/img/webRTC/user.png`;
    let noMic = document.createElement('img');
    noMic.setAttribute('class', 'mic_off');
    noMic.src = `https://www.vchatcloud.com/chat-demo/iframe/iframe_rtc_1/img/webRTC/nosound.png`;
    myCam.append(noCamera);
    myCam.append(noMic);
    channel.setRTCLocalMedia(myVideo);
  });
}

// 공통 코드 =====================================
const vChatCloud = new VChatCloud();

let channel, // joinRoom() 내부에서 채널 객체를 저장할 곳
  userNick = "hello", // 접속자의 닉네임, 사용자에게 입력받은 값을 사용해도 된다.
  userKey; // 접속자 고유 키



// rtc
let myCam, remoteCamList;

window.addEventListener("load", function () {
  // 접속자의 미디어 소스를 보여줄 위치
  $('.cam').hide(); // 캠 화면 출력
  myCam = $(".my_cam");
  myCam.attr("name", "my_cam");
  remoteCamList = $(".remote_cam_wrap");
  $('.login').submit(function (e) {
    e.preventDefault();
    userNick = $('.login input').val(); // 사용자가 입력한 닉네임 설정
    $('.login').hide(); // 로그인 폼 숨김
    $('.cam').show(); // 캠 화면 출력
    joinRoom(
      channelKey, // CMS에서 발급받은 키 값
      // 사용자의 고유 키 값 생성
      "xxxxxxxx".replace(/[xy]/g, function (a, b) {
        return (
          (b = Math.random() * 16), (a == "y" ? (b & 3) | 8 : b | 0).toString(16)
        );
      }),
      userNick, // 유저 닉네임
      // 채널 생성 후 실행할 콜백 함수
      function (err, history) {
        if (err) {
          openError(err.code, function () {
            vChatCloud.disconnect();
          });
        } else {
          videoInit();
        }
      }
    );
  });
  $(".exit").click(function () {
    exit();
  });
});

function joinRoom(roomId, clientKey, nickName, callback) {
  // vchatcloud 객체
  channel = vChatCloud.joinChannel(
    {
      roomId: roomId,
      clientKey: clientKey,
      nickName: nickName
    },
    function (error, history) {
      $(
        "div.entery, div.chatout, div.notice, div.whisper, div.content"
      ).remove();
      if (error) {
        if (callback) return callback(error, null);
        return error;
      }
      if (callback) callback(null, history);
      // 채팅영역에 글쓰기가 활성화될시 활성화
      if (typeof write == "function")
        write(
          "실시간 채팅에 오신 것을 환영합니다. 개인정보를 보호하고 커뮤니티 가이드를 준수하는 것을 잊지 마세요!",
          "notice"
        );
    }
  );
}


function getRoomInfo() {
  const api_url = "https://vchatcloud.com/api/openapi/getChatRoomInfo";
  let param = {
    room_id: channelKey
  };
  $.post(
    api_url,
    param,
    function (data) {
      if (data.result_cd == 1) {
        console.log(data);
        // $("#roomNm").append(data.param.room_nm);
      } else {
        console.log("조회 실패");
        oastPopup("조회 실패");
      }
    },
    "json"
  );
}

function openError(code, callback) {
  let p = $("div.errorpopup").hide();
  if (errMsg[code] == undefined) {
    $("p:nth-child(2)", p).text(code);
  } else {
    $("p:nth-child(2)", p).text(errMsg[code].kor);
  }
  $("a", p)
    .off()
    .click(function () {
      p.hide();
      if (typeof callback == "function") {
        callback();
      }
    });
  p.show();
}

// 마이크 온/오프
function mic_on_off(item) {
  if (channel) {
    var chk = $(item).attr("class");
    var img = $(item).children("img")[0];
    var cam_mic = $("div[name=my_cam]").children(".mic_off")[0];
    if (chk == "mic btn_on") {
      // 마이크 끄기
      channel.toggleRTCAudioControl(false);
      $(item).attr("class", "mic btn_off");
      $(img).attr(
        "src",
        "https://www.vchatcloud.com/chat-demo/iframe/iframe_rtc_1/img/webRTC/off_mic.png"
      );
      $(cam_mic).show();
    } else {
      // 마이크 켜기
      channel.toggleRTCAudioControl(true);
      $(item).attr("class", "mic btn_on");
      $(img).attr(
        "src",
        "https://www.vchatcloud.com/chat-demo/iframe/iframe_rtc_1/img/webRTC/on_mic.png"
      );
      $(cam_mic).hide();
    }
  } else {
    // 로그인이 필요함
  }
}

// 카메라 온/오프
function cam_on_off(item) {
  if (channel) {
    var chk = $(item).attr("class");
    var img = $(item).children("img")[0];
    var video = $("div[name=my_cam]").children("div.camvideo")[0];

    if (chk == "cam btn_on") {
      // 카메라 끄기
      channel.toggleRTCVideoControl(false);
      $(item).attr("class", "cam btn_off");
      $(img).attr(
        "src",
        "https://www.vchatcloud.com/chat-demo/iframe/iframe_rtc_1/img/webRTC/off_cam.png"
      );
    } else {
      // 카메라 끄기
      channel.toggleRTCVideoControl(true);
      $(item).attr("class", "cam btn_on");
      $(img).attr(
        "src",
        "https://www.vchatcloud.com/chat-demo/iframe/iframe_rtc_1/img/webRTC/on_cam.png"
      );
      // 카메라 켜짐
    }
  } else {
    // 미 로그인 상태
  }
}

function exit() {
  if (channel && confirm("종료 하시겠습니까?")) {
    $(".cam-footer .cam-btn .mic").off("click.rtc");
    $(".cam-footer .cam-btn .cam").off("click.rtc");
    vChatCloud.disconnect();
    channel = undefined;
    $('.login').show(); // 로그인 폼 숨김
    $('.cam').hide(); // 캠 화면 출력
  } else {
    // 로그인 되지 않았음!
  }
}
Run Pen

External CSS

  1. https://fonts.googleapis.com/css2?family=Noto+Sans+KR:wght@100;300;400;500;700;900&amp;display=swap
  2. https://www.vchatcloud.com/chat-demo/iframe/iframe_rtc_2/css/reset.css

External JavaScript

  1. https://code.jquery.com/jquery-3.5.1.min.js
  2. https://code.jquery.com/ui/1.12.1/themes/base/jquery-ui.css
  3. https://kit.fontawesome.com/c13f14f04a.js
  4. https://cdnjs.cloudflare.com/ajax/libs/webrtc-adapter/8.1.0/adapter.min.js
  5. https://cdn.jsdelivr.net/npm/sockjs-client@1.5.0/dist/sockjs.min.js
  6. https://www.vchatcloud.com/chat-demo/iframe/iframe_rtc_2/js/vchatcloud-1.2.0.min.js
  7. https://www.vchatcloud.com/chat-demo/iframe/iframe_rtc_2/js/errMsg.js