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

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

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

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

جاده getUserMedia()

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

چندین نوع از "API های ضبط رسانه" در چند سال گذشته تکامل یافته اند. بسیاری از افراد نیاز به دسترسی به دستگاه های بومی را در وب تشخیص دادند، اما این امر باعث شد همه و مادرشان مشخصات جدیدی را جمع آوری کنند. اوضاع آنقدر به هم ریخت که سرانجام W3C تصمیم گرفت یک گروه کاری تشکیل دهد. تنها هدف آنها؟ دیوانگی را معنا کن! گروه کاری Device APIs Policy (DAP) وظیفه ادغام + استاندارد کردن تعداد زیادی از پیشنهادات را بر عهده دارد.

من سعی خواهم کرد آنچه را که در سال 2011 اتفاق افتاد خلاصه کنم…

دور 1: ضبط رسانه HTML

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">

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

پشتیبانی:

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

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

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

Opera یکی از اولین مرورگرهایی بود که پیاده سازی های اولیه فیلم برداری را بر اساس عنصر <device> ایجاد کرد. بلافاصله پس از آن (به طور دقیق در همان روز )، WhatWG تصمیم گرفت تگ <device> را به نفع یک آپدیت دیگر حذف کند، این بار یک API جاوا اسکریپت به نام navigator.getUserMedia() . یک هفته بعد، اپرا بیلدهای جدیدی را منتشر کرد که شامل پشتیبانی از مشخصات getUserMedia() به روز شده بود. در اواخر همان سال، مایکروسافت با انتشار آزمایشگاهی برای 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> را شامل نشده است. حدس می‌زنم یک API کمتر برای نگرانی :) <device> دو چیز عالی داشت: 1.) معنایی بود و 2.) برای پشتیبانی از دستگاه‌های صوتی/تصویری به راحتی قابل گسترش بود.

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

دور 3: WebRTC

عنصر <device> در نهایت راه Dodo را طی کرد.

سرعت یافتن یک API عکسبرداری مناسب به لطف تلاش بزرگتر WebRTC (ارتباطات زمان واقعی وب) تسریع شد. این مشخصات توسط گروه کاری W3C WebRTC نظارت می شود. گوگل، اپرا، موزیلا و چند مورد دیگر پیاده سازی هایی دارند.

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>

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

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

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

متد 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 آمده است:

گفتگوی مجوز در کروم
گفتگوی مجوز در کروم

ارائه بازگشت

برای کاربرانی که از navigator.mediaDevices.getUserMedia() پشتیبانی نمی‌کنند، یکی از گزینه‌ها این است که اگر API پشتیبانی نمی‌شود و/یا تماس به دلایلی انجام نشد، به فایل ویدیویی موجود برگردند:

if (!navigator.mediaDevices?.getUserMedia) {
  video.src = "fallbackvideo.webm";
} else {
  const stream = await navigator.mediaDevices.getUserMedia({ video: true });
  video.srcObject = stream;
}