(تقريبا) بيڪار ويب ڪيم برائوزر کان اسٽريمنگ. ميڊيا اسٽريم ۽ ويب ساکٽ

هن آرٽيڪل ۾ مان حصيداري ڪرڻ چاهيان ٿو وڊيو اسٽريم ڪرڻ جي منهنجي ڪوششن کي ويب ساکٽ ذريعي بغير ٽئين پارٽي برائوزر پلگ ان استعمال ڪرڻ جهڙوڪ Adobe Flash Player. انهي کي ڳولڻ لاء پڙهو ته ان مان ڇا آيو.

Adobe Flash، اڳ ۾ Macromedia Flash، ايپليڪيشن ٺاهڻ لاءِ هڪ پليٽ فارم آهي جيڪي ويب برائوزر ۾ هلن ٿيون. ميڊيا اسٽريم API جي تعارف کان اڳ، اهو عملي طور تي ويب ڪيم کان وڊيو ۽ آواز اسٽريمنگ لاءِ واحد پليٽ فارم هو، انهي سان گڏ برائوزر ۾ مختلف قسم جون ڪانفرنسون ۽ چيٽ ٺاهڻ لاءِ. ميڊيا جي معلومات جي منتقلي لاءِ پروٽوڪول RTMP (Real Time Messaging Protocol) اصل ۾ هڪ ڊگهي وقت لاءِ بند هو، جنهن جو مطلب هو: جيڪڏهن توهان چاهيو ٿا ته پنهنجي اسٽريمنگ سروس کي بهتر بڻائي، ته مهرباني ڪري پاڻ کي Adobe مان سافٽ ويئر استعمال ڪرڻ لاءِ مهرباني ڪريو - Adobe Media Server (AMS).

2012 ۾ ڪجهه وقت کان پوء، Adobe عوام کي "ڇڏي ڇڏيو ۽ ان کي ختم ڪيو". وضاحت RTMP پروٽوڪول، جنهن ۾ غلطيون هيون ۽ لازمي طور تي نامڪمل هو. ان وقت تائين، ڊولپرز هن پروٽوڪول جي پنهنجي عمل کي ٺاهڻ شروع ڪيو، ۽ Wowza سرور ظاهر ٿيو. 2011 ۾، Adobe RTMP سان لاڳاپيل پيٽرن جي غير قانوني استعمال لاء Wowza جي خلاف هڪ ڪيس داخل ڪيو؛ 4 سالن کان پوء، تڪرار حل ڪيو ويو.

ايڊوب فليش پليٽ فارم 20 سالن کان وڌيڪ پراڻو آهي، جنهن دوران ڪيتريون ئي نازڪ خاميون دريافت ڪيون ويون آهن، سپورٽ وعدو ڪيو 2020 تائين ختم ٿيڻ لاءِ، اسٽريمنگ سروس لاءِ ڪجھ متبادل ڇڏيندي.

منهنجي منصوبي لاء، مون فوري طور تي برائوزر ۾ فليش جي استعمال کي مڪمل طور تي ختم ڪرڻ جو فيصلو ڪيو. مون مٿي بيان ڪيو بنيادي سبب؛ فليش موبائل پليٽ فارمن تي به سپورٽ نه آهي، ۽ مان واقعي نه ٿي چاهيان ته ايڊوب فليش کي ونڊوز تي ڊولپمينٽ لاءِ (وائن ايموليٽر). تنهن ڪري مان جاوا اسڪرپٽ ۾ ڪلائنٽ لکڻ لاءِ نڪتو. اهو صرف هڪ پروٽوٽائپ هوندو، ڇاڪاڻ ته بعد ۾ مون کي معلوم ٿيو ته اسٽريمنگ تمام گهڻي موثر طريقي سان ٿي سگهي ٿي p2p جي بنياد تي، صرف منهنجي لاءِ اهو پير - سرور - پيرس هوندو، پر ان کان وڌيڪ ڪنهن ٻئي وقت، ڇاڪاڻ ته اهو اڃا تيار ناهي.

شروع ڪرڻ لاء، اسان کي حقيقي ويب ساکٽ سرور جي ضرورت آهي. مون ميلوڊي گو پيڪيج جي بنياد تي آسان ترين ٺاهيو:

سرور ڪوڊ

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

         // Бродкастим видео поток 
	m.HandleMessageBinary(func(s *melody.Session, msg []byte) {
		m.BroadcastBinary(msg)
	})

	log.Println("Starting server...")

	http.ListenAndServe(":3000", r)
}

ڪلائنٽ تي (اسٽريمنگ پاسي)، توهان کي پهريون ڀيرو ڪئميرا تائين رسائي جي ضرورت آهي. هن ذريعي ڪيو ويندو آهي MediaStream API.

اسان حاصل ڪريون ٿا رسائي (اجازت) ذريعي ڪئميرا / مائڪروفون تائين ميڊيا ڊوائيسز API. هي API هڪ طريقو مهيا ڪري ٿو MediaDevices.getUserMedia()، جيڪو پاپ اپ ڏيکاري ٿو. هڪ ونڊو صارف کان ڪئميرا ۽/يا مائڪروفون تائين رسائي جي اجازت لاءِ پڇي ٿي. مان اهو نوٽ ڪرڻ چاهيان ٿو ته مون گوگل ڪروم ۾ سڀ تجربا ڪيا، پر مان سمجهان ٿو ته فائر فاکس ۾ سڀ ڪجهه ساڳيو ڪم ڪندو.

اڳيون، 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');

// если доступен MediaDevices API, пытаемся получить доступ к камере (можно еще и к микрофону)
// getUserMedia вернет обещание, на которое подписываемся и полученный видеопоток в колбеке направляем в video объект на странице

if (navigator.mediaDevices.getUserMedia) {
        navigator.mediaDevices.getUserMedia({video: true}).then(function (stream) {
          // видео поток привязываем к video тегу, чтобы клиент мог видеть себя и контролировать 
          video.srcObject = stream;
        });
}
</script>

ساکٽ تي وڊيو اسٽريم کي نشر ڪرڻ لاءِ، توهان کي ان کي ڪٿي انڪوڊ ڪرڻ، ان کي بفر ڪرڻ ۽ ان کي حصن ۾ منتقل ڪرڻ جي ضرورت آهي. خام وڊيو وهڪرو ويب ساکٽ ذريعي منتقل نه ٿي ڪري سگھجي. اهو آهي جتي اهو اسان جي مدد لاء اچي ٿو MediaRecorder API. هي API توهان کي انڪوڊ ڪرڻ ۽ اسٽريم کي ٽڪرن ۾ ٽوڙڻ جي اجازت ڏئي ٿو. مان نيٽ ورڪ تي گهٽ بائيٽ موڪلڻ لاءِ وڊيو اسٽريم کي ڪمپريس ڪرڻ لاءِ انڪوڊنگ ڪندو آهيان. ان کي ٽڪرن ۾ ٽوڙي ڇڏيو، توهان هر ٽڪرا ويب ساکٽ ڏانهن موڪلي سگهو ٿا. ڪوڊ:

اسان وڊيو اسٽريم کي انڪوڊ ڪيو، ان کي ٽڪر ۾ ٽوڙيو

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

// если доступен MediaDevices API, пытаемся получить доступ к камере (можно еще и к микрофону)
// getUserMedia вернет обещание, на которое подписываемся и полученный видеопоток в колбеке направляем в video объект на странице

if (navigator.mediaDevices.getUserMedia) {
        navigator.mediaDevices.getUserMedia({video: true}).then(function (stream) {
          // видео поток привязываем к video тегу, чтобы клиент мог видеть себя и контролировать 
          video.srcObject = s;
          var
            recorderOptions = {
                mimeType: 'video/webm; codecs=vp8' // будем кодировать видеопоток в формат webm кодеком vp8
              },
              mediaRecorder = new MediaRecorder(s, recorderOptions ); // объект MediaRecorder

               mediaRecorder.ondataavailable = function(e) {
                if (e.data && e.data.size > 0) {
                  // получаем кусочек видеопотока в e.data
                }
            }

            mediaRecorder.start(100); // делит поток на кусочки по 100 мс каждый

        });
}
</script>

هاڻي اچو ته ويب ساکٽ ذريعي ٽرانسميشن شامل ڪريو. حيرت انگيز طور تي، توهان سڀني کي هن جي ضرورت آهي هڪ اعتراض آهي ويب ساکٽ. ان ۾ صرف ٻه طريقا آهن موڪلڻ ۽ بند ڪرڻ. نالا پاڻ لاءِ ڳالهائين ٿا. شامل ڪيل ڪوڊ:

اسان وڊيو اسٽريم کي سرور ڏانهن منتقل ڪريون ٿا

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

// если доступен MediaDevices API, пытаемся получить доступ к камере (можно еще и к микрофону)
// getUserMedia вернет обещание, на которое подписываемся и полученный видеопоток в колбеке направляем в video объект на странице

if (navigator.mediaDevices.getUserMedia) {
        navigator.mediaDevices.getUserMedia({video: true}).then(function (stream) {
          // видео поток привязываем к video тегу, чтобы клиент мог видеть себя и контролировать 
          video.srcObject = s;
          var
            recorderOptions = {
                mimeType: 'video/webm; codecs=vp8' // будем кодировать видеопоток в формат webm кодеком vp8
              },
              mediaRecorder = new MediaRecorder(s, recorderOptions ), // объект MediaRecorder
              socket = new WebSocket('ws://127.0.0.1:3000/ws');

               mediaRecorder.ondataavailable = function(e) {
                if (e.data && e.data.size > 0) {
                  // получаем кусочек видеопотока в e.data
                 socket.send(e.data);
                }
            }

            mediaRecorder.start(100); // делит поток на кусочки по 100 мс каждый

        }).catch(function (err) { console.log(err); });
}
</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) {
                // "кладем" полученный кусочек в массив 
                arrayOfBlobs.push(event.data);
                // здесь будем читать кусочки
                readChunk();
            });
</script>

هڪ ڊگهي وقت تائين مون اهو سمجهڻ جي ڪوشش ڪئي ته ڇو حاصل ٿيل ٽڪرا فوري طور تي پلے بیک لاء وڊيو عنصر ڏانهن موڪلڻ ناممڪن آهي، پر اهو ظاهر ٿيو ته اهو نه ٿو ڪري سگهجي، يقينا، توهان کي لازمي طور تي ٽڪرا هڪ خاص بفر ۾ رکڻ گهرجي. وڊيو عنصر، ۽ صرف ان کان پوء اهو وڊيو وهڪرو کيڏڻ شروع ڪندو. هن لاء توهان جي ضرورت پوندي MediaSource API и FileReader API.

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(), // объект MediaSource
        vid2url = URL.createObjectURL(mediaSource), // создаем объект URL для связывания видеопотока с проигрывателем
        arrayOfBlobs = [],
        sourceBuffer = null; // буфер, пока нуль-объект

         socket.addEventListener('message', function (event) {
                // "кладем" полученный кусочек в массив 
                arrayOfBlobs.push(event.data);
                // здесь будем читать кусочки
                readChunk();
            });

         // как только MediaSource будет оповещен , что источник готов отдавать кусочки 
        // видео/аудио потока
        // создаем буфер , следует обратить внимание, что буфер должен знать в каком формате 
        // каким кодеком был закодирован поток, чтобы тем же способом прочитать видеопоток
         mediaSource.addEventListener('sourceopen', function() {
            var mediaSource = this;
            sourceBuffer = mediaSource.addSourceBuffer("video/webm; codecs="vp8"");
        });

      function readChunk() {
        var reader = new FileReader();
        reader.onload = function(e) { 
          // как только FileReader будет готов, и загрузит себе кусочек видеопотока
          // мы "прицепляем" перекодированный в Uint8Array (был Blob) кусочек в буфер, связанный
          // с проигрывателем, и проигрыватель начинает воспроизводить полученный кусочек видео/аудио
          sourceBuffer.appendBuffer(new Uint8Array(e.target.result));

          reader.onload = null;
        }
        reader.readAsArrayBuffer(arrayOfBlobs.shift());
      }
</script>

اسٽريمنگ سروس جو پروٽوٽائپ تيار آهي. مکيه نقصان اهو آهي ته وڊيو پلے بیک 100 ms ذريعي منتقلي واري پاسي کان پوئتي رهجي ويندي؛ اسان اهو پاڻ کي سيٽ ڪيو جڏهن وڊيو اسٽريم کي ورهائڻ کان پهريان ان کي سرور ڏانهن منتقل ڪيو وڃي. ان کان علاوه، جڏهن مون پنهنجي ليپ ٽاپ تي چيڪ ڪيو، منتقلي ۽ وصول ڪندڙ پاسن جي وچ ۾ دير سان گڏ ٿي وئي، اهو واضح طور تي نظر اچي رهيو هو. مون ان نقصان کي دور ڪرڻ جا طريقا ڳولڻ شروع ڪيا، ۽... RTCPeerConnection API، جيڪو توهان کي اجازت ڏئي ٿو وڊيو اسٽريم کي منتقل ڪرڻ جي بغير چالن جي جيئن ته وهڪرو کي ٽڪرن ۾ ورهائڻ. جمع ٿيڻ وارو وقفو، مان سمجهان ٿو، حقيقت اها آهي ته برائوزر هر ٽڪرا کي ٻيهر انڪوڊ ڪري ٿو ويب فارميٽ ۾ ٽرانسميشن کان اڳ. مون وڌيڪ کوٽائي نه ڪئي، پر WebRTC جو مطالعو شروع ڪيو. مان سمجهان ٿو ته جيڪڏهن مون کي ڪميونٽي لاءِ دلچسپ لڳي ته مان پنهنجي تحقيق جي نتيجن بابت هڪ الڳ مضمون لکندس.

جو ذريعو: www.habr.com

تبصرو شامل ڪريو