درحال ضبط ویدیو از کاربر

Mat Scales

اکنون بسیاری از مرورگرها می‌توانند به ورودی صوتی و ویدیویی کاربر دسترسی داشته باشند. بااین‌حال، بسته به مرورگر، ممکن است تجربه‌ای کاملاً پویا و درون‌خطی باشد، یا ممکن است به برنامه دیگری در دستگاه کاربر واگذار شود.

ساده و به‌تدریج شروع کنید

ساده‌ترین کار این است که از کاربر بخواهید فایل ازپیش ضبط‌شده‌ای را ارائه دهد. این کار را با ایجاد یک عنصر ورودی فایل ساده و افزودن فیلتر accept که نشان می‌دهد فقط می‌توانیم فایل‌های ویدیویی را بپذیریم و مشخصه capture که نشان می‌دهد می‌خواهیم آن را مستقیماً از دوربین دریافت کنیم، انجام دهید.

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

این روش در همه پلاتفرم‌ها کار می‌کند. در رایانه، از کاربر خواسته می‌شود فایلی را از سیستم فایل بارگذاری کند (ویژگی capture نادیده گرفته می‌شود). در Safari در iOS، برنامه دوربین باز می‌شود و به شما امکان می‌دهد ویدیو ضبط کنید و سپس آن را به صفحه وب برگردانید؛ در Android، به کاربر امکان می‌دهد انتخاب کند که از کدام برنامه برای ضبط ویدیو استفاده کند و سپس آن را به صفحه وب برگرداند.

بسیاری از دستگاه‌های همراه بیش‌از یک دوربین دارند. اگر اولویتی دارید، می‌توانید مشخصه 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، آن را در سرور بارگذاری کنید
  • قاب‌ها را در بوم بکشید و فیلترها را روی آن اعمال کنید

درحالی‌که استفاده از روش عنصر ورودی برای دسترسی به داده‌های ویدیو همه‌جا رایج است، این گزینه کمترین جذابیت را دارد. ما واقعاً می‌خواهیم به دوربین دسترسی داشته باشیم و تجربه خوبی را مستقیماً در صفحه ارائه دهیم.

دسترسی به دوربین به‌صورت تعاملی

مرورگرهای مدرن می‌توانند خط مستقیمی به دوربین داشته باشند که به ما امکان می‌دهد تجربه‌هایی بسازیم که کاملاً با صفحه وب ادغام شده‌اند و کاربر هرگز مرورگر را ترک نخواهد کرد.

دسترسی به دوربین را دریافت کنید

بااستفاده از میانای برنامه‌سازی کاربردی در مشخصات WebRTC به‌نام getUserMedia() می‌توانیم مستقیماً به دوربین دسترسی پیدا کنیم. getUserMedia() از کاربر درخواست می‌کند به میکروفون‌ها و دوربین‌های متصل‌شده دسترسی داشته باشد.

اگر موفقیت‌آمیز باشد، میانای برنامه‌سازی کاربردی یک Stream برمی‌گرداند که حاوی داده‌های دوربین یا میکروفون است و سپس می‌توانیم آن را به عنصر <video> متصل کنیم، به جاری‌سازی WebRTC متصل کنیم، یا بااستفاده از میانای برنامه‌سازی کاربردی MediaRecorder ذخیره کنیم.

برای دریافت داده از دوربین، فقط video: true را در شیء محدودیت‌ها که به میانای برنامه‌سازی کاربردی getUserMedia() ارسال می‌شود تنظیم می‌کنیم

<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');
});

سپس می‌توانید deviceId موردنظرتان را برای استفاده هنگام تماس با getUserMedia ارسال کنید.

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

این به‌تنهایی چندان مفید نیست. تنها کاری که می‌توانیم انجام دهیم این است که داده‌های ویدیو را بگیریم و آن را بازپخش کنیم.

دسترسی به داده‌های خام از دوربین

برای دسترسی به داده‌های ویدیویی خام از دوربین، می‌توانید هر فریم را در <canvas> بکشید و پیکسل‌ها را مستقیماً دستکاری کنید.

برای بوم ۲بعدی می‌توانید از روش drawImage بافت استفاده کنید تا قاب فعلی عنصر <video> را در بوم بکشید.

context.drawImage(myVideoElement, 0, 0);

با بوم WebGL می‌توانید از عنصر <video> به‌عنوان منبع بافت استفاده کنید.

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

توجه داشته باشید که در هر دو مورد، از قاب فعلی ویدیو درحال پخش استفاده خواهد شد. برای پردازش چند قاب، باید هر بار ویدیو را در بوم بازطراحی کنید.

در مقاله ما درباره اعمال جلوه‌های هم‌زمان روی تصاویر و ویدیو می‌توانید اطلاعات بیشتری دراین‌باره کسب کنید.

ذخیره داده‌ها از دوربین

ساده‌ترین راه برای ذخیره کردن داده‌های دوربین استفاده از MediaRecorder API است.

‫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 را فراخوانی کنید، مرورگر از کاربر درخواست می‌کند به سایت شما اجازه دسترسی به دوربین را بدهد.

کاربران از اینکه برای دسترسی به دستگاه‌های قدرتمند در ماشینشان از آن‌ها درخواست شود متنفرند و اغلب درخواست را مسدود می‌کنند، یا اگر زمینه ایجاد درخواست را درک نکنند، آن را نادیده می‌گیرند. بهترین روش این است که فقط وقتی برای اولین‌بار نیاز است، درخواست دسترسی به دوربین شود. وقتی کاربر دسترسی را اعطا کرد، دیگر از او درخواست نمی‌شود، اما اگر دسترسی را رد کند، نمی‌توانید دوباره برای درخواست اجازه از کاربر دسترسی دریافت کنید.

از میانای برنامه‌سازی کاربردی اجازه‌ها استفاده کنید تا بررسی کنید آیا ازقبل دسترسی دارید یا نه

‫getUserMedia API به شما اطلاع نمی‌دهد که آیا قبلاً به دوربین دسترسی داشته‌اید یا نه. این امر مشکلی را برای شما ایجاد می‌کند، برای ارائه یک واسط کاربر خوب برای اینکه کاربر به شما اجازه دسترسی به دوربین را بدهد، باید درخواست دسترسی به دوربین را مطرح کنید.

این مشکل را می‌توان در برخی‌از مرورگرها بااستفاده از Permission 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 () {};
});

بازخورد