การบันทึกวิดีโอจากผู้ใช้

เครื่องชั่งน้ำหนัก

ขณะนี้เบราว์เซอร์จำนวนมากสามารถเข้าถึงอินพุตวิดีโอและเสียงจากผู้ใช้ได้แล้ว อย่างไรก็ตาม เบราว์เซอร์อาจเป็นประสบการณ์แบบไดนามิกและอินไลน์อย่างเต็มรูปแบบ หรืออาจมีการมอบสิทธิ์ให้กับแอปอื่นในอุปกรณ์ของผู้ใช้ ทั้งนี้ขึ้นอยู่กับเบราว์เซอร์ของคุณ

เริ่มต้นอย่างง่ายๆ และค่อยๆ เติบโต

วิธีที่ง่ายที่สุดคือขอไฟล์ที่บันทึกไว้ล่วงหน้าจากผู้ใช้ คุณสามารถทำได้โดยการสร้างองค์ประกอบอินพุตไฟล์แบบง่ายและเพิ่มตัวกรอง accept ที่บ่งชี้ว่าเรารับเฉพาะไฟล์วิดีโอและแอตทริบิวต์ capture ที่ระบุว่าเราต้องการรับไฟล์ได้โดยตรงจากกล้อง

<input type="file" accept="video/*" capture />

วิธีนี้ใช้งานได้ในทุกแพลตฟอร์ม ในเดสก์ท็อป ระบบจะแจ้งให้ผู้ใช้อัปโหลดไฟล์จากระบบไฟล์ (ไม่สนใจแอตทริบิวต์ capture) ใน Safari บน iOS แอปกล้องจะเปิดแอปกล้องขึ้นมา ให้คุณบันทึกวิดีโอแล้วส่งกลับไปยังหน้าเว็บ ส่วนใน Android ผู้ใช้จะเลือกได้ว่าจะใช้แอปใดบันทึกวิดีโอก่อนส่งกลับไปยังหน้าเว็บ

อุปกรณ์เคลื่อนที่จำนวนมากมีกล้องมากกว่า 1 ตัว หากต้องการ คุณตั้งค่าแอตทริบิวต์ capture เป็น user หากต้องการใช้กล้องที่หันหน้าเข้าหาผู้ใช้ หรือ environment หากต้องการกล้องที่หันออกด้านนอก

<input type="file" accept="video/*" capture="user" />
<input type="file" accept="video/*" capture="environment" />

โปรดทราบว่านี่เป็นเพียงคำแนะนำ หากเบราว์เซอร์ไม่รองรับตัวเลือกนี้ หรือประเภทกล้องที่คุณขอไม่พร้อมใช้งาน เบราว์เซอร์อาจเลือกกล้องอื่น

เมื่อผู้ใช้บันทึกเสร็จแล้วและกลับมาที่เว็บไซต์อีก คุณจะต้องเก็บข้อมูลไฟล์ คุณเข้าถึงอย่างรวดเร็วได้โดยแนบเหตุการณ์ onchange กับองค์ประกอบอินพุต จากนั้นอ่านพร็อพเพอร์ตี้ files ของออบเจ็กต์เหตุการณ์

<input type="file" accept="video/*" capture="camera" id="recorder" />
<video id="player" controls></video>
<script>
  var recorder = document.getElementById('recorder');
  var player = document.getElementById('player');

  recorder.addEventListener('change', function (e) {
    var file = e.target.files[0];
    // Do something with the video file.
    player.src = URL.createObjectURL(file);
  });
</script>

เมื่อเข้าถึงไฟล์แล้ว คุณจะดำเนินการกับไฟล์ได้ตามต้องการ ตัวอย่างเช่น คุณสามารถทำสิ่งต่อไปนี้ได้

  • แนบไปกับองค์ประกอบ <video> โดยตรงเพื่อให้คุณเล่นได้
  • ดาวน์โหลดลงในอุปกรณ์ของผู้ใช้
  • อัปโหลดไปยังเซิร์ฟเวอร์โดยแนบกับ XMLHttpRequest
  • วาดเฟรมลงในภาพพิมพ์แคนวาสแล้วใส่ฟิลเตอร์

แม้ว่าการใช้วิธีขององค์ประกอบอินพุตเพื่อรับสิทธิ์เข้าถึงข้อมูลวิดีโอจะเป็นอย่างแพร่หลาย แต่วิธีนี้ก็เป็นตัวเลือกที่น่าสนใจน้อยที่สุด เราต้องการเข้าถึงกล้องและ มอบประสบการณ์การใช้งานที่ดีในหน้าเว็บโดยตรง

เข้าถึงกล้องแบบอินเทอร์แอกทีฟ

เบราว์เซอร์ที่ทันสมัยสามารถต่อสายตรงไปยังกล้องช่วยให้เราสร้างประสบการณ์ที่ผสานรวมเข้ากับหน้าเว็บได้อย่างเต็มที่โดยที่ผู้ใช้จะไม่ต้องออกจากเบราว์เซอร์

รับสิทธิ์เข้าถึงกล้อง

เราเข้าถึงกล้องได้โดยตรงโดยใช้ API ในข้อกำหนดของ WebRTC ที่ชื่อ getUserMedia() getUserMedia() จะแจ้งให้ผู้ใช้ เข้าถึงไมโครโฟนและกล้องที่เชื่อมต่ออยู่

หากดำเนินการสำเร็จ API จะส่งคืน Stream ที่จะมีข้อมูลจากกล้องหรือไมโครโฟน จากนั้นเราสามารถต่อเชื่อมกับองค์ประกอบ <video>, ต่อเชื่อมกับสตรีม WebRTC หรือบันทึกโดยใช้ MediaRecorder API

หากต้องการรับข้อมูลจากกล้อง เราเพิ่งตั้งค่า video: true ในออบเจ็กต์ข้อจำกัดที่ส่งไปยัง getUserMedia() API

<video id="player" controls></video>
<script>
  var player = document.getElementById('player');

  var handleSuccess = function (stream) {
    player.srcObject = stream;
  };

  navigator.mediaDevices
    .getUserMedia({audio: true, video: true})
    .then(handleSuccess);
</script>

หากคุณต้องการเลือกกล้องตัวใดตัวหนึ่ง คุณสามารถแจกแจงกล้องที่มีอยู่ก่อน

navigator.mediaDevices.enumerateDevices().then((devices) => {
  devices = devices.filter((d) => d.kind === 'videoinput');
});

จากนั้นคุณจะส่งรหัสอุปกรณ์ที่ต้องการใช้เมื่อโทรหา getUserMedia ได้

navigator.mediaDevices.getUserMedia({
  audio: true,
  video: {
    deviceId: devices[0].deviceId,
  },
});

จริงๆ แล้ว การดำเนินการนี้ไม่มีประโยชน์ สิ่งที่เราทำได้มีเพียงนำข้อมูลวิดีโอมาเล่น

เข้าถึงข้อมูลดิบจากกล้อง

หากต้องการเข้าถึงข้อมูลวิดีโอดิบจากกล้อง คุณสามารถวาดแต่ละเฟรมลงใน <canvas> แล้วจัดการพิกเซลได้โดยตรง

สำหรับ Canvas แบบ 2 มิติ คุณใช้เมธอด drawImage ของบริบทเพื่อวาดเฟรมปัจจุบันขององค์ประกอบ <video> ลงในผืนผ้าใบได้

context.drawImage(myVideoElement, 0, 0);

เมื่อใช้ Canvas ของ WebGL คุณสามารถใช้องค์ประกอบ <video> เป็นต้นฉบับของพื้นผิวได้

gl.texImage2D(
  gl.TEXTURE_2D,
  0,
  gl.RGBA,
  gl.RGBA,
  gl.UNSIGNED_BYTE,
  myVideoElement,
);

โปรดทราบว่าทั้ง 2 กรณีนี้จะใช้เฟรมปัจจุบันของวิดีโอที่กำลังเล่น หากต้องการประมวลผลหลายเฟรม คุณต้องวาดวิดีโอซ้ำบนผืนผ้าใบทุกครั้ง

ดูข้อมูลเพิ่มเติมเกี่ยวกับเรื่องนี้ได้ในบทความการใช้เอฟเฟกต์แบบเรียลไทม์กับรูปภาพและวิดีโอ

บันทึกข้อมูลจากกล้อง

วิธีที่ง่ายที่สุดในการบันทึกข้อมูลจากกล้องคือการใช้ API ของ MediaRecorder

MediaRecorder API จะใช้สตรีมที่สร้างโดย getUserMedia แล้วบันทึกข้อมูลจากสตรีมไปยังปลายทางที่ต้องการอย่างต่อเนื่อง

<a id="download">Download</a>
<button id="stop">Stop</button>
<script>
  let shouldStop = false;
  let stopped = false;
  const downloadLink = document.getElementById('download');
  const stopButton = document.getElementById('stop');

  stopButton.addEventListener('click', function() {
    shouldStop = true;
  })

  var handleSuccess = function(stream) {
    const options = {mimeType: 'video/webm'};
    const recordedChunks = [];
    const mediaRecorder = new MediaRecorder(stream, options);

    mediaRecorder.addEventListener('dataavailable', function(e) {
      if (e.data.size > 0) {
        recordedChunks.push(e.data);
      }

      if(shouldStop === true && stopped === false) {
        mediaRecorder.stop();
        stopped = true;
      }
    });

    mediaRecorder.addEventListener('stop', function() {
      downloadLink.href = URL.createObjectURL(new Blob(recordedChunks));
      downloadLink.download = 'acetest.webm';
    });

    mediaRecorder.start();
  };

  navigator.mediaDevices.getUserMedia({ audio: true, video: true })
      .then(handleSuccess);
</script>

ในกรณีของเรา เราจะบันทึกข้อมูลลงในอาร์เรย์โดยตรง ซึ่งในภายหลังเราจะนำไปเปลี่ยนเป็น Blob ซึ่งสามารถใช้เพื่อบันทึกลงในเว็บเซิร์ฟเวอร์หรือในพื้นที่เก็บข้อมูลของผู้ใช้โดยตรง

ขออนุญาตใช้กล้องอย่างมีความรับผิดชอบ

หากผู้ใช้ยังไม่เคยให้สิทธิ์เว็บไซต์เข้าถึงกล้องมาก่อน ทันทีที่คุณเรียกใช้ getUserMedia เบราว์เซอร์จะแจ้งให้ผู้ใช้ให้สิทธิ์กล้องแก่เว็บไซต์

มีการแสดงความเกลียดชังของผู้ใช้เมื่อได้รับแจ้งเพื่อเข้าถึงอุปกรณ์ที่มีประสิทธิภาพในเครื่องของตน และผู้ใช้จะบล็อกคำขออยู่บ่อยครั้ง หรืออาจจะเพิกเฉยต่อคำขอหากไม่เข้าใจบริบทของข้อความแจ้งที่ถูกสร้างขึ้น แนวทางปฏิบัติแนะนำคือ การขอสิทธิ์เข้าถึงกล้องในครั้งแรกที่จำเป็นเท่านั้น เมื่อผู้ใช้ให้สิทธิ์การเข้าถึงแล้ว ผู้ใช้จะไม่ถูกขออีก อย่างไรก็ตาม หากผู้ใช้ปฏิเสธการเข้าถึงแล้ว คุณจะไม่สามารถขอสิทธิ์การเข้าถึงจากผู้ใช้ได้อีก

ใช้ API สิทธิ์เพื่อตรวจสอบว่าคุณมีสิทธิ์เข้าถึงอยู่แล้วหรือไม่

getUserMedia API จะไม่แจ้งให้คุณทราบว่าคุณมีสิทธิ์เข้าถึงกล้องอยู่แล้วหรือไม่ ซึ่งจะทำให้เกิดปัญหาในการมอบ UI ที่ดีเพื่อให้ผู้ใช้ให้สิทธิ์เข้าถึงกล้องแก่คุณ คุณจะต้องขอสิทธิ์เข้าถึงกล้อง

ซึ่งแก้ไขได้ในบางเบราว์เซอร์โดยใช้ Permission API API navigator.permission ช่วยให้คุณค้นหาสถานะความสามารถในการเข้าถึง API บางรายการได้โดยไม่ต้องแสดงข้อความแจ้งอีกครั้ง

หากต้องการค้นหาว่าคุณมีสิทธิ์เข้าถึงกล้องของผู้ใช้หรือไม่ ให้ส่ง {name: 'camera'} ไปยังเมธอดการค้นหา แล้วระบบจะแสดงข้อผิดพลาดอย่างใดอย่างหนึ่งต่อไปนี้

  • granted — ผู้ใช้ได้ให้สิทธิ์คุณเข้าถึงกล้องไว้ก่อนหน้านี้
  • prompt — ผู้ใช้ไม่ได้ให้สิทธิ์เข้าถึงแก่คุณและจะได้รับข้อความแจ้งเมื่อคุณโทรหา getUserMedia
  • denied — ระบบหรือผู้ใช้บล็อกการเข้าถึงกล้องอย่างชัดแจ้งและคุณจะเข้าถึงกล้องไม่ได้

และตอนนี้คุณสามารถตรวจสอบได้อย่างรวดเร็วเพื่อดูว่าจำเป็นต้องแก้ไขอินเทอร์เฟซผู้ใช้เพื่อให้เหมาะสมกับการดำเนินการต่างๆ ที่ผู้ใช้ต้องทำหรือไม่

navigator.permissions.query({name: 'camera'}).then(function (result) {
  if (result.state == 'granted') {
  } else if (result.state == 'prompt') {
  } else if (result.state == 'denied') {
  }
  result.onchange = function () {};
});

ความคิดเห็น