التقاط الصوت والفيديو بتنسيق HTML5

لطالما كان تسجيل الصوت أو الفيديو الهدف الذي يسعى إليه مطوّرو الويب. كان علينا لعدة سنوات الاعتماد على المكوّنات الإضافية للمتصفّح (Flash أو Silverlight) لإنجاز المهام. تعالَ،

HTML5 لإنقاذ الموقف قد لا يكون ذلك واضحًا، ولكنّ استخدام HTML5 بشكل متزايد قد أدّى إلى زيادة كبيرة في إمكانية الوصول إلى أجهزة الأجهزة. ومن الأمثلة المثالية على ذلك الموقع الجغرافي (نظام تحديد المواقع العالمي)، وOrientation API (مقياس التسارع)، وWebGL (وحدة معالجة الرسومات)، وWeb Audio API (أجهزة الصوت). هذه الميزات قوية جدًا، إذ توفّر واجهات برمجة تطبيقات JavaScript عالية المستوى التي تستند إلى إمكانات الأجهزة الأساسية للنظام.

يقدّم هذا الدليل التعليمي واجهة برمجة تطبيقات جديدة، وهي GetUserMedia، التي تسمح لتطبيقات الويب بالوصول إلى كاميرا المستخدم وميكروفونه.

الطريق إلى getUserMedia()‎

إذا لم تكن على دراية بتاريخ واجهة برمجة التطبيقات هذه، ننصحك بالاطّلاع على قصة إنشاء getUserMedia() API.

تم تطوير العديد من الصيغ لواجهات برمجة التطبيقات Media Capture API خلال السنوات القليلة الماضية. أدرك العديد من الأشخاص الحاجة إلى إمكانية الوصول إلى الأجهزة الأصلية على الويب، ولكن أدى ذلك إلى أن يضع الجميع مواصفات جديدة. ازدادت الأمور بهمجيةً لدرجة أنّ W3C قرّرت أخيرًا تشكيل مجموعة عمل. ما هو الغرض الوحيد من هذه الميزة؟ فهم هذا السلوك الغريب تم تكليف مجموعة عمل سياسة واجهات برمجة تطبيقات الأجهزة (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">

يبدو هذا جيدًا، أليس كذلك؟ أُعجب بشكل خاص بإعادة استخدام إدخال ملف. من الناحية الدلالية، هذا منطقي جدًا. لا تتيح هذه "واجهة برمجة التطبيقات" إمكانية تطبيق تأثيرات في الوقت الفعلي (مثل عرض بيانات كاميرا الويب المباشرة على <canvas> وتطبيق فلاتر WebGL). لا يسمح لك عنصر HTML Media Capture إلا بتسجيل ملف وسائط أو أخذ لقطة في الوقت المحدّد.

الدعم:

  • متصفّح Android 3.0: أحد أوائل عمليات التنفيذ يمكنك مشاهدة هذا الفيديو لمعرفة كيفية تنفيذ ذلك.
  • Chrome لنظام التشغيل Android (0.16)
  • Firefox Mobile 10.0
  • متصفّحَا Safari وChrome على نظام التشغيل iOS6 (توافق جزئي)

الجولة 2: عنصر الجهاز

اعتقد الكثيرون أنّ ميزة "التقاط الوسائط باستخدام HTML" كانت محدودة جدًا، لذا تم طرح مواصفات جديدة تتيح استخدام أي نوع من الأجهزة (المستقبلية). ليس من المستغرب أنّ التصميم تطلّب استخدام عنصر جديد، وهو عنصر <device>، الذي أصبح سلف getUserMedia().

كان Opera من أوائل المتصفّحات التي أنشأت عمليات التنفيذ الأولية لميزة تسجيل الفيديو استنادًا إلى عنصر <device>. بعد ذلك بوقت قصير، (في اليوم نفسه على وجه التحديد)، قرّرت مجموعة WhatWG إلغاء علامة <device> لصالح علامة أخرى رائجة، وهي هذه المرة واجهة برمجة تطبيقات JavaScript تُسمى navigator.getUserMedia(). بعد أسبوع، طرحت Opera إصدارات جديدة تتضمّن توافقًا مع مواصفات getUserMedia() المعدَّلة. وفي وقت لاحق من ذلك العام، انضمت Microsoft إلى المجموعة من خلال إصدار إصدار تجريبي من 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> في أي إصدار من متصفّحات الويب. يبدو أنّه تمّ إيقاف واجهة برمجة تطبيقات واحدة، أليس كذلك؟ :) كانت واجهة برمجة التطبيقات <device> تتمتع بخاصيتين رائعتين: 1) كانت دلالية، و2) كان من السهل توسيع نطاقها لتتيح استخدام أكثر من أجهزة الصوت/الفيديو فقط.

خذ نفسًا عميقًا. تتغيّر هذه الأمور بسرعة.

الجولة 3: WebRTC

تم إيقاف عنصر <device> نهائيًا.

تم تسريع وتيرة العثور على واجهة برمجة تطبيقات مناسبة لتسجيل المحتوى بفضل الجهود المبذولة في WebRTC (التواصل في الوقت الفعلي على الويب). تتولى مجموعة عمل WebRTC في W3C الإشراف على هذه المواصفات. تتوفّر تطبيقات Google وOpera وMozilla وبعض التطبيقات الأخرى.

يرتبط getUserMedia() بـ WebRTC لأنّه البوابة إلى هذه المجموعة من واجهات برمجة التطبيقات. وتوفّر هذه الأذونات وسائل الوصول إلى بث الكاميرا أو الميكروفون على الجهاز.

الدعم:

يتوفّر getUserMedia() منذ الإصدار 21 من Chrome والإصدار 18 من Opera والإصدار 17 من Firefox.

الخطوات الأولى

باستخدام 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 الجديدة معًا. ويعمل هذا العنصر مع <audio> و<video>، وهما عنصران آخران من عناصر HTML5. يُرجى ملاحظة أنّنا لا نضبط سمة src أو نُدرِج عناصر <source> في عنصر <video>. بدلاً من إضافة عنوان URL لملف وسائط إلى الفيديو، سنضبط srcObject على عنصر LocalMediaStream الذي يمثّل كاميرا الويب.

أطلب أيضًا من <video> إلى autoplay، وإلا سيتم تجميده في الإطار الأول. تعمل إضافة controls أيضًا على النحو المتوقّع.

ضبط قيود الوسائط (الدقة والارتفاع والعرض)

يمكن أيضًا استخدام المَعلمة الأولى في getUserMedia() لتحديد المزيد من المتطلبات (أو القيود) على بث الوسائط الذي يتم إرجاعه. على سبيل المثال، بدلاً من الإشارة إلى أنّك تريد الوصول الأساسي إلى الفيديو (مثل {video: true})، يمكنك أيضًا طلب أن يكون البث عالي الدقة:

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

لمزيد من الإعدادات، يُرجى الاطّلاع على Constraints API.

اختيار مصدر وسائط

تطلب طريقة enumerateDevices() لواجهة MediaDevices قائمة بأجهزة إدخال وإخراج الوسائط المتاحة، مثل الميكروفونات والكاميرات وسماعات الرأس وما إلى ذلك. يتم حلّ الوعد الذي تم إرجاعه باستخدام صفيف من عناصر 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);
}

يمكنك الاطّلاع على العرض التوضيحي الرائع الذي أعدّه "سام دوتون" حول كيفية السماح للمستخدمين باختيار مصدر الوسائط.

الأمان

تعرِض المتصفّحات مربّع حوار أذونات عند استدعاء navigator.mediaDevices.getUserMedia()، ما يمنح المستخدمين خيار منح الإذن بالوصول إلى الكاميرا أو الميكروفون أو رفضه. على سبيل المثال، إليك مربّع حوار أذونات Chrome:

مربّع حوار الأذونات في Chrome
مربّع حوار الأذونات في Chrome

توفير عنصر احتياطي

بالنسبة إلى المستخدمين الذين لا يتوفّر لديهم navigator.mediaDevices.getUserMedia()، يمكنهم استخدام ملف فيديو حالي بدلاً من ذلك في حال عدم توفّر واجهة برمجة التطبيقات و/أو تعذّر إرسال الطلب لأي سبب:

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