(beagnach) ceamara gréasáin gan úsáid ag sruthú ó bhrabhsálaí. Sruth na Meán agus Soicéid Ghréasáin

San Airteagal seo ba mhaith liom mo chuid iarrachtaí físeáin a shruthú trí shoicéid gréasáin a roinnt gan úsáid a bhaint as forlíontáin brabhsálaí tríú páirtí ar nós Adobe Flash Player. Léigh ar aghaidh chun a fháil amach cad a tháinig de.

Is ardán é Adobe Flash, Macromedia Flash roimhe seo, chun feidhmchláir a chruthú a ritheann i mbrabhsálaí gréasáin. Sular tugadh isteach an Media Stream API, ba é an t-aon ardán a bhí ann chun físeáin agus guth a shruthú ó cheamara gréasáin, chomh maith le cineálacha éagsúla comhdhálacha agus comhráite a chruthú sa bhrabhsálaí. Dúnadh an prótacal maidir le faisnéis meán a tharchur RTMP (Prótacal Teachtaireachtaí Fíor-Ama) ar feadh i bhfad, rud a chiallaigh: más mian leat do sheirbhís sruthú a threisiú, bí cineálta go leor chun bogearraí ó Adobe féin a úsáid - Adobe Media Server (AMS).

Tar éis roinnt ama in 2012, “thug Adobe suas é agus scar sé amach” don phobal. sonraíocht Prótacal RTMP, ina raibh earráidí agus a bhí neamhiomlán go bunúsach. Faoin am sin, thosaigh na forbróirí ag déanamh a gcur i bhfeidhm féin den phrótacal seo, agus bhí an chuma ar fhreastalaí Wowza. In 2011, chomhdaigh Adobe a lawsuit i gcoinne Wowza maidir le húsáid mhídhleathach paitinní a bhaineann le RTMP; tar éis 4 bliana, réitíodh an choimhlint go cairdiúil.

Tá ardán Adobe Flash níos mó ná 20 bliain d'aois, agus le linn an ama sin aimsíodh go leor leochaileachtaí ríthábhachtacha, tacaíocht gheall chun deiridh faoi 2020, rud a fhágann nach bhfuil mórán roghanna eile ann don tseirbhís sruthaithe.

Maidir le mo thionscadal, chinn mé láithreach úsáid Flash sa bhrabhsálaí a thréigean go hiomlán. Thug mé le fios an phríomhchúis thuas; ní thacaítear le Flash freisin ar ardáin soghluaiste, agus níor theastaigh uaim Adobe Flash a imscaradh le haghaidh forbartha ar Windows (aithriseoir fíona). Mar sin leag mé amach cliant a scríobh i JavaScript. Ní bheidh anseo ach fréamhshamhail, mar níos déanaí d'fhoghlaim mé gur féidir sruthú a dhéanamh i bhfad níos éifeachtaí bunaithe ar p2p, ach amháin domsa is piaraí - freastalaí - piaraí a bheidh ann, ach níos mó air sin uair eile, toisc nach bhfuil sé réidh fós.

Chun tús a chur leis, ní mór dúinn an freastalaí gréasáin soicéad iarbhír. Rinne mé an ceann is simplí bunaithe ar an bpacáiste melody go:

Cóid freastalaí

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

Ar an gcliant (taobh sruthú), ní mór duit rochtain a fháil ar an gceamara ar dtús. Déantar é seo trí MediaStream API.

Faighimid rochtain (cead) ar an gceamara/micreafón tríd Media Devices API. Soláthraíonn an API seo modh MediaDevices.getUserMedia(), a thaispeánann aníos. fuinneog a iarrann cead ar an úsáideoir rochtain a fháil ar an gceamara agus/nó ar an micreafón. Ba mhaith liom a thabhairt faoi deara go ndearna mé na turgnaimh go léir i Google Chrome, ach is dóigh liom go n-oibreoidh gach rud mar an gcéanna i Firefox.

Ansin, seolann getUserMedia() Gealltanas, a dtéann sé thar réad MediaStream chuige - sruth de shonraí físe-fuaime. Sannaimid an réad seo d'airí src na heiliminte físeáin. Cód:

Taobh craolacháin

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

Chun sruth físeáin a chraoladh thar soicéid, ní mór duit é a ionchódú áit éigin, é a mhaolánú, agus é a tharchur i gcodanna. Ní féidir an sruth físe amh a tharchur trí shoicéid gréasáin. Seo an áit a dtagann sé chun ár gcabhair MediaRecorder API. Ligeann an API seo duit an sruth a ionchódú agus a bhriseadh ina phíosaí. Déanaim ionchódú chun an sruth físeáin a chomhbhrú chun níos lú beart a sheoladh thar an líonra. Tar éis duit é a bhriseadh ina phíosaí, is féidir leat gach píosa a sheoladh chuig soicéad gréasáin. Cód:

Ionchódaíonn muid an sruth físeáin, briseann sé ina phíosaí é

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

Anois cuirimis tarchur trí shoicéid ghréasáin. Ionadh, tá gach atá uait le haghaidh an réad WebSocket. Tá sé ach dhá mhodh a sheoladh agus a dhúnadh. Labhraíonn na hainmneacha ar a son féin. Cód curtha leis:

Déanaimid an sruth físeáin a tharchur chuig an bhfreastalaí

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

Tá an taobh craolta réidh! Anois déanaimis iarracht sruth físeáin a fháil agus é a thaispeáint ar an gcliant. Cad atá de dhíth orainn le haghaidh seo? Gcéad dul síos, ar ndóigh, an nasc soicéad. Ceangaimid “éisteoir” den réad WebSocket agus déanaimid liostáil don imeacht ‘teachtaireacht’. Tar éis píosa sonraí dénártha a fháil, craolann ár bhfreastalaí é do shíntiúsóirí, is é sin, cliaint. Sa chás seo, spreagtar an fheidhm aisghlao a bhaineann le “éisteoir” an imeachta ‘teachtaireachta’ ar an gcliant;

Glacaimid le sruth físeáin

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

Ar feadh i bhfad rinne mé iarracht a thuiscint cén fáth go bhfuil sé dodhéanta na píosaí faighte a sheoladh láithreach chuig an eilimint físe le haghaidh athsheinm, ach d'éirigh sé amach nach féidir é seo a dhéanamh, ar ndóigh, ní mór duit an píosa a chur i maolán speisialta ar dtús. an eilimint físe, agus ansin amháin a thosóidh sé ag imirt an tsrutha físeáin. Chun seo beidh ort MediaSource API и FileReader API.

Feidhmíonn MediaSource mar chineál idirghabhálaí idir réad athsheinm na meán agus foinse an tsrutha meán seo. Tá maolán inphlugáilte san oibiacht MediaSource le haghaidh foinse an tsrutha físe/fuaime. Gné amháin is ea nach féidir leis an maolán ach sonraí Uint8 a choinneáil, mar sin beidh FileReader uait chun maolán den sórt sin a chruthú. Féach ar an gcód agus beidh sé níos soiléire:

Sruth físeán a imirt

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

Tá fréamhshamhail na seirbhíse sruthú réidh. Is é an príomh-mhíbhuntáiste ná go mbeidh athsheinm físe 100 ms taobh thiar den taobh tarchurtha; Thairis sin, nuair a sheiceáil mé ar mo ríomhaire glúine, de réir a chéile carntha an t-aga moille idir na taobhanna tarchuir agus glactha, bhí sé seo le feiceáil go soiléir. Thosaigh mé ag lorg bealaí chun an míbhuntáiste seo a shárú, agus... tháinig mé trasna RTCPeerConnection API, a ligeann duit sruth físeáin a tharchur gan cleasanna ar nós an sruth a roinnt ina phíosaí. Is é is cúis leis an moill charntha, dar liom, ná go ndéanann an brabhsálaí gach píosa a athchódú isteach san fhormáid webm roimh an tarchur. Níor thochail mé a thuilleadh, ach thosaigh mé ag déanamh staidéir ar WebRTC is dóigh liom go scríobhfaidh mé alt ar leith faoi thorthaí mo thaighde má cheapann mé go bhfuil sé suimiúil don phobal.

Foinse: will.com

Add a comment