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

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

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

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

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

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

تم تطوير العديد من الصيغ لواجهات برمجة التطبيقات 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);

لمزيد من الإعدادات، يُرجى الاطّلاع على Restrictts 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;
}