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

مقدمة

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

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

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

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

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

تطورت عدة صيغ من "واجهات برمجة تطبيقات Media Capture" خلال السنوات القليلة الماضية. أدرك الكثيرون الحاجة إلى إمكانية الوصول إلى الأجهزة الأصلية على الويب، ولكن أدى ذلك إلى أن يضع الجميع مواصفات جديدة، وأصبحت الأمور فوضوية للغاية إلى أن قررت W3C أخيرًا تشكيل فريق عمل. ما هو الغرض الوحيد من ذلك؟ استفِد من هذه الميزة! تم تكليف فريق عمل "سياسة واجهات برمجة التطبيقات الخاصة بالأجهزة" (DAP) بدمج مجموعة كبيرة من الاقتراحات وتوحيدها.

سأحاول تلخيص ما حدث في عام 2011…

الجولة 1: 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" تسجيل ملف وسائط أو التقاط لقطة في الوقت الحالي فقط.

الدعم:

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

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

رأى الكثيرون أنّ مواصفات HTML Media Capture كانت محدودة جدًا، لذا ظهرت مواصفات جديدة تتيح استخدام أي نوع من الأجهزة (في المستقبل). وليس من المستغرب أنّ التصميم تطلّب عنصرًا جديدًا، وهو عنصر <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>

حسنًا. إذًا، ما الذي يحدث هنا؟ تُعدّ ميزة &quot;التقاط الوسائط&quot; مثالاً ممتازًا على كيفية عمل واجهات برمجة التطبيقات الجديدة في HTML5 معًا. تعمل هذه السمة بالتزامن مع سمات HTML5 الأخرى، <audio> و<video>. لاحظ أنّنا لا نضبط السمة 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);

للاطّلاع على المزيد من عمليات الضبط، يُرجى الرجوع إلى واجهة برمجة التطبيقات الخاصة بالقيود.

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

تطلب طريقة 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);
}

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

الأمان

تعرض المتصفّحات مربّع حوار الأذونات عند طلب 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;
}