(Cha mhòr) camara-lìn gun fheum a’ sruthadh bho bhrobhsair. Sruth Meadhanan agus Websockets

San artaigil seo tha mi airson na h-oidhirpean agam gus bhidio a shruthladh tro websockets a cho-roinn gun a bhith a’ cleachdadh plugins brabhsair treas-phàrtaidh leithid Adobe Flash Player. Leugh air adhart gus faighinn a-mach dè a thàinig às.

Tha Adobe Flash, Macromedia Flash roimhe seo, na àrd-ùrlar airson tagraidhean a chruthachadh a bhios a’ ruith ann am brobhsair lìn. Mus deach an Media Stream API a thoirt a-steach, b’ e cha mhòr an aon àrd-ùrlar airson sruthadh bhidio is guth bho chamara-lìn, a bharrachd air a bhith a’ cruthachadh diofar sheòrsan cho-labhairtean agus chòmhraidhean sa bhrobhsair. Chaidh am protocol airson fiosrachadh meadhanan a sgaoileadh RTMP (Pròtacal Teachdaireachdan Fìor-ùine) a dhùnadh airson ùine mhòr, a bha a’ ciallachadh: ma tha thu airson an t-seirbheis sruthadh agad a bhrosnachadh, bi coibhneil gu leòr airson bathar-bog Adobe fhèin a chleachdadh - Adobe Media Server (AMS).

Às deidh beagan ùine ann an 2012, thug Adobe “a-mach agus sgap e” don phoball. sònrachadh protocol RTMP, anns an robh mearachdan agus gu ìre mhòr neo-choileanta. Mun àm sin, thòisich luchd-leasachaidh air na gnìomhan aca fhèin a dhèanamh den phròtacal seo, agus nochd am frithealaiche Wowza. Ann an 2011, chuir Adobe cùis-lagha a-steach an-aghaidh Wowza airson cleachdadh mì-laghail de phaitinnean co-cheangailte ri RTMP; às deidh 4 bliadhna, chaidh a’ chòmhstri fhuasgladh gu càirdeil.

Tha an àrd-ùrlar Adobe Flash còrr is 20 bliadhna a dh'aois, agus rè na h-ùine sin chaidh mòran de chugallachd èiginneach a lorg, taic gheall gu crìch ro 2020, a’ fàgail glè bheag de roghainnean eile airson an t-seirbheis sruthadh.

Airson mo phròiseact, cho-dhùin mi sa bhad stad a chuir air cleachdadh Flash sa bhrobhsair. Chomharraich mi am prìomh adhbhar gu h-àrd; Chan eil taic ri Flash idir air àrd-ùrlaran gluasadach, agus cha robh mi dha-rìribh ag iarraidh Adobe Flash a chleachdadh airson leasachadh air Windows (emuladair fìona). Mar sin chuir mi romham teachdaiche a sgrìobhadh ann an JavaScript. Is e dìreach prototype a bhios ann, oir nas fhaide air adhart dh’ ionnsaich mi gum faodar sruthadh a dhèanamh tòrr nas èifeachdaiche stèidhichte air p2p, dìreach dhòmhsa bidh e na cho-aoisean - frithealaiche - co-aoisean, ach barrachd air an sin uair eile, leis nach eil e deiseil fhathast.

Airson tòiseachadh, feumaidh sinn an fhìor fhrithealaiche websockets. Rinn mi am fear as sìmplidh stèidhichte air pasgan fonn:

Còd an fhrithealaiche

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

Air an neach-dèiligidh (taobh sruthadh), feumaidh tu faighinn chun chamara an-toiseach. Tha seo air a dhèanamh troimhe MediaStream API.

Gheibh sinn cothrom (cead) air a’ chamara/micreofon troimhe Media Devices API. Tha an API seo a’ toirt seachad dòigh MediaDevices.getUserMedia(), a tha a’ sealltainn popup. uinneag ag iarraidh cead air a’ chleachdaiche cothrom fhaighinn air a’ chamara agus/no am micreofon. Bu mhath leam a thoirt fa-near gun do rinn mi na deuchainnean gu lèir ann an Google Chrome, ach tha mi a’ smaoineachadh gun obraich a h-uile dad mun aon rud ann am Firefox.

An uairsin, bidh getUserMedia () a’ tilleadh Gealladh, dha bheil e a’ dol seachad air rud MediaStream - sruth de dhàta claisneachd-bhidio. Sònraichidh sinn an nì seo do sheilbh src na h-eileamaid bhidio. Còd:

Taobh craolaidh

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

Gus sruth bhidio a chraoladh thairis air socaidean, feumaidh tu a chòdachadh an àiteigin, bufair e, agus cuir thairis e ann am pàirtean. Chan urrainnear an sruth bhidio amh a chraoladh tro websockets. Seo far a bheil e a’ tighinn gu ar taic MediaRecorder API. Leigidh an API seo leat an sruth a chòdachadh agus a bhriseadh na phìosan. Bidh mi a’ còdachadh gus an sruth bhidio a dhlùthadh gus nas lugha de bytes a chuir thairis air an lìonra. An dèidh a bhriseadh na phìosan, faodaidh tu gach pìos a chuir gu socaid lìn. Còd:

Bidh sinn a’ còdachadh an t-sruth bhidio, ga bhriseadh na phìosan

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

A-nis leig dhuinn tar-chuir a chuir tro websockets. Gu h-iongantach, chan eil agad ach rud a tha a dhìth ort airson seo WebSockets. Chan eil ann ach dà dhòigh cur is dùnadh. Bidh na h-ainmean a’ bruidhinn air an son fhèin. Còd air a chur ris:

Bidh sinn a’ tar-chuir an t-sruth bhidio chun t-seirbheisiche

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

Tha an taobh craolaidh deiseil! A-nis feuchaidh sinn ri sruth bhidio fhaighinn agus a thaisbeanadh air an neach-dèiligidh. Dè tha a dhìth oirnn airson seo? An toiseach, gu dearbh, an ceangal socaid. Bidh sinn a’ ceangal “neach-èisteachd” ris an nì WebSocket agus a’ fo-sgrìobhadh don tachartas ‘teachdaireachd’. Às deidh dhuinn pìos dàta dà-chànanach fhaighinn, bidh an frithealaiche againn ga chraoladh gu luchd-aontachaidh, is e sin, teachdaichean. Anns a’ chùis seo, tha an gnìomh gairm air ais co-cheangailte ri “neach-èisteachd” an tachartais ‘teachdaireachd’ air a phiobrachadh air an neach-dèiligidh; tha an nì fhèin air a chuir a-steach don argamaid gnìomh - pìos den t-sruth bhidio air a chòdachadh le vp8.

Gabhaidh sinn ri sruth bhidio

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

Airson ùine mhòr dh'fheuch mi ri tuigsinn carson a tha e do-dhèanta na pìosan a fhuaireadh a chuir chun eileamaid bhidio airson ath-chluich, ach thionndaidh e a-mach nach urrainnear seo a dhèanamh, gu dearbh, feumaidh tu an toiseach am pìos a chuir ann am bufair sònraichte ceangailte ri an eileamaid bhidio, agus dìreach an uairsin a thòisicheas e a’ cluich an t-sruth bhidio. Airson seo bidh feum agad air MediaSource API и FileReader API.

Tha MediaSource ag obair mar sheòrsa de eadar-mheadhanair eadar cuspair ath-chluich mheadhanan agus stòr an t-sruth mheadhanan seo. Anns an nì MediaSource tha bufair pluggable airson stòr an t-sruth bhidio / claisneachd. Is e aon fheart nach urrainn don bufair ach dàta Uint8 a chumail, agus mar sin bidh feum agad air FileReader gus a leithid de bhufair a chruthachadh. Thoir sùil air a’ chòd agus fàsaidh e nas soilleire:

A ' cluich an t-sruth video

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

Tha am prototype den t-seirbheis sruthadh deiseil. Is e am prìomh ana-cothrom gum bi ath-chluich bhidio air dheireadh air an taobh tar-chuir le 100 ms; shuidhich sinn seo sinn fhìn nuair a bhios sinn a ’roinn an t-sruth bhidio mus cuir sinn chun t-seirbheisiche e. A bharrachd air an sin, nuair a rinn mi sgrùdadh air an laptop agam, chruinnich an t-ìsleachadh eadar na taobhan tar-chuir agus faighinn mean air mhean, bha seo ri fhaicinn gu soilleir. Thòisich mi a 'coimhead airson dòighean gus faighinn thairis air an ana-cothrom seo, agus thàinig ... RTCPeerConnection API, a leigeas leat sruth bhidio a chuir thairis gun chleasan mar a bhith a’ sgoltadh an t-sruth gu pìosan. Tha an dàil cruinneachaidh, tha mi a’ smaoineachadh, mar thoradh air gu bheil am brabhsair ag ath-chòdachadh gach pìos gu cruth webm mus tèid a chraoladh. Cha do chladhaich mi tuilleadh, ach thòisich mi a’ sgrùdadh WebRTC.Tha mi a’ smaoineachadh gun sgrìobh mi artaigil air leth mu thoraidhean an rannsachaidh agam ma bhios e inntinneach dhomh don choimhearsnachd.

Source: www.habr.com

Cuir beachd ann