في هذه المقالة ، أرغب في مشاركة محاولاتي لدفق الفيديو عبر مجموعات الويب دون استخدام إضافات مستعرضات خارجية مثل Adobe Flash Player. ما جاء من هذا قرأ على.
Adobe Flash - سابقًا Macromedia Flash ، عبارة عن منصة لإنشاء التطبيقات التي تعمل في متصفح الويب. قبل تنفيذ واجهة برمجة تطبيقات Media Stream ، كان هذا هو النظام الأساسي الوحيد لدفق الفيديو والصوت من كاميرا ويب ، وكذلك لإنشاء مؤتمرات ودردشات متعددة في المتصفح. تم بالفعل إغلاق بروتوكول نقل معلومات الوسائط RTMP (بروتوكول المراسلة في الوقت الحقيقي) لفترة طويلة ، مما يعني: إذا كنت ترغب في رفع خدمة البث ، فيرجى استخدام البرنامج من Adobe نفسه - Adobe Media Server (AMS).
بعد مرور بعض الوقت في عام 2012 ، قام Adobe "بالاستسلام والبصق"
لمواصفات بروتوكول RTMP ، الذي تضمن أخطاء ، وفي الواقع ، لم يكتمل. بحلول ذلك الوقت ، بدأ المطورون في تنفيذ تطبيقاتهم لهذا البروتوكول ، لذلك ظهر خادم Wowza. في عام 2011 ، رفعت Adobe دعوى قضائية ضد Wowza للاستخدام غير القانوني لبراءات الاختراع المتعلقة بـ RTMP ، بعد 4 سنوات تم حل النزاع من قبل العالم.
ظل نظام Adobe Flash موجودًا منذ أكثر من 20 عامًا ، وخلال هذا الوقت تم اكتشاف العديد من الثغرات الحرجة ،
ووعدوا بالتوقف عن الدعم بحلول عام 2020 ، لذلك لا توجد بدائل كثيرة لخدمة البث.
بالنسبة لمشروعي ، قررت على الفور التخلي تمامًا عن استخدام Flash في المتصفح. السبب الرئيسي الذي أشرت إليه أعلاه ، هو أيضًا Flash غير مدعوم على الإطلاق على منصات الجوّال ، ولم أرغب حقًا في نشر Adobe Flash لتطويره على نظام windows (محاكي النبيذ). لذلك بدأت في كتابة عميل في JavaScript. سيكون هذا مجرد نموذج أولي ، كما علمت لاحقًا أن التدفق يمكن أن يتم بشكل أكثر فاعلية استنادًا إلى p2p ، فقط سيكون لدي نظراء خادم نظير ، لكن أكثر في ذلك الوقت الآخر ، لأنه ليس جاهزًا بعد.
للبدء ، نحتاج إلى خادم websockets نفسه. لقد صنعت أبسطها بناءً على باقة لحن الذهاب:
كود الخادمpackage main import ( "errors" "github.com/go-chi/chi" "gopkg.in/olahol/melody.v1" "log" "net/http" "time" ) func main() { r := chi.NewRouter() m := melody.New() m.Config.MaxMessageSize = 204800 r.Get("/", func(w http.ResponseWriter, r *http.Request) { http.ServeFile(w, r, "public/index.html") }) r.Get("/ws", func(w http.ResponseWriter, r *http.Request) { m.HandleRequest(w, r) })
على العميل (جانب البث) ، يجب عليك أولاً الوصول إلى الكاميرا. يتم ذلك من خلال
واجهة برمجة تطبيقات MediaStream .
يمكننا الوصول (الدقة) إلى الكاميرا / الميكروفون من خلال
واجهة برمجة تطبيقات أجهزة الوسائط . يوفر API هذا طريقة
MediaDevices.getUserMedia () ، والتي تعرض نافذة منبثقة. نافذة تطلب من المستخدم الإذن للوصول إلى الكاميرا و / أو الميكروفون. أود أن أشير إلى أنني أجريت جميع التجارب في Google Chrome ، ولكن أعتقد أن كل شيء في Firefox سيعمل بنفس الطريقة تقريبًا.
بعد ذلك ، تقوم getUserMedia () بإرجاع وعد ، يتم فيه تمرير كائن MediaStream - دفق من بيانات الفيديو والصوت. نقوم بتعيين هذا الكائن إلى خاصية عنصر الفيديو في src. كود:
جانب البث <style> #videoObjectHtml5ApiServer { width: 320px; height: 240px; background: #666; } </style> </head> <body> <video autoplay id="videoObjectHtml5ApiServer"></video> <script type="application/javascript"> var video = document.getElementById('videoObjectHtml5ApiServer'); </script>
لبث دفق فيديو عبر مآخذ توصيل ، تحتاج إلى ترميزه بطريقة ما في مكان ما ، وتخزينه مؤقتًا ونقله في أجزاء. لا يمكن نقل دفق الفيديو الخام عبر websockets. هذا هو
المكان الذي يأتي
فيه تطبيق MediaRecorder API . تسمح لك واجهة برمجة التطبيقات هذه بترميز وتقسيم الدفق إلى أجزاء. أقوم بالتشفير لضغط دفق الفيديو ، حتى أتمكن من دفع بايت أقل عبر الشبكة. بعد تقسيمها إلى أجزاء ، يمكن إرسال كل قطعة إلى websocket. كود:
نقوم بتشفير دفق الفيديو ، وفازناه على أجزاء <style> #videoObjectHtml5ApiServer { width: 320px; height: 240px; background: #666; } </style> </head> <body> <video autoplay id="videoObjectHtml5ApiServer"></video> <script type="application/javascript"> var video = document.getElementById('videoObjectHtml5ApiServer'); </script>
أضف الآن النقل على websockets. المثير للدهشة ، وهذا يتطلب فقط كائن
WebSocket . لديها طريقتين فقط للإغلاق والإغلاق. الأسماء تتحدث عن نفسها. كود ممتد:
ننقل دفق الفيديو إلى الخادم <style> #videoObjectHtml5ApiServer { width: 320px; height: 240px; background: #666; } </style> </head> <body> <video autoplay id="videoObjectHtml5ApiServer"></video> <script type="application/javascript"> var video = document.getElementById('videoObjectHtml5ApiServer'); </script>
جانب البث جاهز! الآن دعونا نحاول أن نأخذ دفق الفيديو وإظهاره على العميل. ماذا نحتاج لهذا؟ بادئ ذي بدء ، بالطبع ، اتصال المقبس. نحن شنق "المستمع" على كائن WebSocket ، والاشتراك في الحدث "رسالة". بعد تلقي جزء من البيانات الثنائية ، يقوم خادمنا ببثها إلى المشتركين ، أي العملاء. في الوقت نفسه ، يتم تشغيل وظيفة رد الاتصال المتصلة بـ "المستمع" لحدث "الرسالة" على العميل ، ويتم تمرير الكائن نفسه في وسيطة الوظيفة - جزء دفق الفيديو المشفر بواسطة vp8.
نحن نقبل دفق الفيديو <style> #videoObjectHtml5ApiServer { width: 320px; height: 240px; background: #666; } </style> </head> <body> <video autoplay id="videoObjectHtml5ApiServer"></video> <script type="application/javascript"> var video = document.getElementById('videoObjectHtml5ApiServer'), socket = new WebSocket('ws://127.0.0.1:3000/ws'), arrayOfBlobs = []; socket.addEventListener('message', function (event) { </script>
حاولت لفترة طويلة أن أفهم سبب تعذر إرسال القطع المستلمة على الفور إلى عنصر الفيديو للتشغيل ، ولكن تبين أن ذلك مستحيل ، بالطبع ، يجب أولاً وضع القطعة في مخزن مؤقت خاص مرفق بعنصر الفيديو ، وعندها فقط سيبدأ تشغيل دفق الفيديو. للقيام بذلك ، تحتاج إلى
واجهة برمجة تطبيقات MediaSource وواجهة برمجة تطبيقات FileReader .
يعمل MediaSource كنوع من الوسيط بين كائن تشغيل الوسائط ومصدر دفق الوسائط هذا. يحتوي كائن MediaSource على مخزن مؤقت قابل للتوصيل لمصدر دفق الفيديو / الصوت. ميزة واحدة هي أن المخزن المؤقت يمكن أن يحتوي فقط على بيانات Uint8 ، لذلك FileReader مطلوب لإنشاء مثل هذا المخزن المؤقت. ألقِ نظرة على الكود وسوف يصبح أكثر وضوحًا:
لعب دفق الفيديو <style> #videoObjectHtml5ApiServer { width: 320px; height: 240px; background: #666; } </style> </head> <body> <video autoplay id="videoObjectHtml5ApiServer"></video> <script type="application/javascript"> var video = document.getElementById('videoObjectHtml5ApiServer'), socket = new WebSocket('ws://127.0.0.1:3000/ws'), mediaSource = new MediaSource(), </script>
النموذج الأولي لخدمة البث جاهز. العيب الرئيسي هو أن تشغيل الفيديو سيكون 100 مللي ثانية وراء جانب الإرسال ، وضعنا ذلك بأنفسنا عند تقسيم دفق الفيديو قبل إرساله إلى الخادم. علاوة على ذلك ، عندما راجعت جهاز الكمبيوتر المحمول الخاص بي ، تراكمت تدريجياً الفجوة بين جانبي الإرسال والاستقبال ، وكان ذلك مرئيًا بوضوح. بدأت أبحث عن طرق للتغلب على هذا القصور ، و ...
واجهت واجهة برمجة تطبيقات RTCPeerConnection ، والتي تتيح لك نقل دفق فيديو دون حيل مثل تقسيم الدفق إلى أجزاء. أعتقد أن التأخر المتراكم يرجع إلى حقيقة أنه في المتصفح ، قبل النقل ، يتم تحويل كل قطعة إلى تنسيق ويب. لم أعد أكثر في البحث ، لكنني بدأت في دراسة WebRTC ، أفكر في نتائج بحثي ، سأكتب مقالة منفصلة إذا وجدت أن هذا المجتمع مثيرًا للاهتمام.