ضبط صدا و ویدیو در HTML5

مقدمه

ضبط صدا/ویدیو برای مدت طولانی «جام مقدس» توسعه وب بوده است. سال‌ها است که برای انجام این کار به افزایه‌های مرورگر (Flash یا Silverlight) متکی هستیم. عجله کنید!

HTML5 به کمک می‌آید. شاید آشکار نباشد، اما ظهور HTML5 باعث افزایش دسترسی به سخت‌افزار دستگاه شده است. مکان‌یابی جغرافیایی (GPS)، Orientation API (شتاب‌سنج)، WebGL (GPU)، و Web Audio API (سخت‌افزار صوتی) نمونه‌های عالی هستند. این ویژگی‌ها به‌طور مضحکی قدرتمند هستند و میاناهای برنامه‌سازی کاربردی جاوا اسکریپت سطح بالایی را نشان می‌دهند که روی قابلیت‌های سخت‌افزاری زیربنایی سیستم قرار دارند.

این آموزش یک API جدید به نام GetUserMedia را معرفی می‌کند که به برنامه‌های وب اجازه می‌دهد به دوربین و میکروفون کاربر دسترسی پیدا کنند.

مسیر رسیدن به getUserMedia()‎

اگر از تاریخچه آن مطلع نیستید، نحوه رسیدن ما به getUserMedia() API داستان جالبی است.

چندین نوع از «Media Capture APIs» درطول چند سال گذشته توسعه یافته است. Many folks recognized the need to be able to access native devices on the web, but that led everyone and their mom to put together a new spec. Things got so messy that the W3C finally decided to form a working group. تنها هدفشان؟ از این دیوانگی سر در بیاورید! گروه کاری خط‌مشی میانای برنامه‌سازی کاربردی دستگاه (DAP) وظیفه دارد تا انبوهی از پیشنهادها را ادغام و استانداردسازی کند.

سعی می‌کنم اتفاقات سال ۲۰۱۱ را خلاصه کنم…

دور ۱: 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">

خوب است، نه؟ به‌ویژه از اینکه ورودی فایل را دوباره استفاده می‌کند خوشم می‌آید. ازنظر معنایی، خیلی منطقی است. جایی که این «میانای برنامه‌سازی کاربردی» خاص کم می‌آورد، توانایی انجام جلوه‌های هم‌زمان است (مثلاً، ارائه داده‌های وب‌بین زنده به <canvas> و اعمال فیلترهای WebGL). HTML Media Capture فقط به شما امکان می‌دهد فایل رسانه‌ای ضبط کنید یا عکس آنی بگیرید.

پشتیبانی:

  • مرورگر Android 3.0 - یکی از اولین پیاده‌سازی‌ها. این ویدیو را ببینید تا آن را در عمل مشاهده کنید.
  • Chrome for Android (0.16)
  • Firefox Mobile 10.0
  • iOS6 Safari و Chrome (پشتیبانی جزئی)

دور ۲: عنصر دستگاه

بسیاری فکر می‌کردند HTML Media Capture بیش‌ازحد محدودکننده است، بنابراین مشخصات جدیدی ظهور کرد که از هر نوع دستگاهی (در آینده) پشتیبانی می‌کرد. جای تعجب نیست که این طراحی به عنصر جدیدی نیاز داشت، عنصر <device>، که پیش‌درآمدی برای getUserMedia() شد.

Opera یکی از اولین مرورگرهایی بود که پیاده‌سازی‌های اولیه ضبط ویدیو را براساس عنصر <device> ایجاد کرد. کمی بعد (دقیقاً همان روز)، WhatWG تصمیم گرفت برچسب <device> را به‌نفع یک تازه‌وارد دیگر کنار بگذارد، این‌بار یک میانای برنامه‌سازی کاربردی جاوا اسکریپت به‌نام 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> نبوده است. One less API to worry about I guess :) <device> did have two great things going for it though: 1.) it was semantic, and 2.) it was easily extendible to support more than just audio/video devices.

نفس عمیقی بکشید. این چیزها سریع حرکت می‌کنند!

دور ۳: WebRTC

عنصر <device> درنهایت منسوخ شد.

سرعت یافتن میانای برنامه‌سازی کاربردی ضبط مناسب به‌لطف تلاش‌های بیشتر WebRTC (ارتباطات هم‌زمان وب) افزایش یافت. این مشخصات تحت نظارت گروه کاری W3C WebRTC است. ‫Google،‏ Opera،‏ Mozilla، و چند شرکت دیگر پیاده‌سازی‌هایی دارند.

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>

تأیید ضربه بزنید. خب، اینجا چه خبر است؟ ضبط رسانه نمونه‌ای عالی از میاناهای برنامه‌سازی کاربردی HTML5 جدید است که باهم کار می‌کنند. این ویژگی در ارتباط با دیگر دوستان HTML5 ما، <audio> و <video>، کار می‌کند. توجه داشته باشید که ما مشخصه src را تنظیم نمی‌کنیم یا عناصر <source> را در عنصر <video> قرار نمی‌دهیم. به‌جای اینکه نشانی وب فایل رسانه‌ای را به ویدیو بدهیم، 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);

برای پیکربندی‌های بیشتر، میانای برنامه‌سازی کاربردی محدودیت‌ها را ببینید.

انتخاب منبع رسانه

روش 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 را می‌بینید:

کادر گفتگوی اجازه در Chrome
کادر گفتگوی اجازه در 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;
}