F'dan l-artikolu nixtieq naqsam it-tentattivi tiegħi biex nistrimja vidjo permezz ta 'websockets mingħajr ma nuża plugins tal-brawżer ta' partijiet terzi bħal Adobe Flash Player. Kompli aqra biex issir taf x'ġara minnha.
Adobe Flash, li qabel kienet Macromedia Flash, hija pjattaforma għall-ħolqien ta' applikazzjonijiet li jaħdmu f'web browser. Qabel l-introduzzjoni tal-Media Stream API, kienet prattikament l-unika pjattaforma għall-istrimjar ta 'vidjow u vuċi minn webcam, kif ukoll għall-ħolqien ta' diversi tipi ta 'konferenzi u chats fil-browser. Il-protokoll għat-trażmissjoni tal-informazzjoni tal-midja RTMP (Real Time Messaging Protocol) kien fil-fatt magħluq għal żmien twil, li fisser: jekk trid issaħħaħ is-servizz tal-istrimjar tiegħek, kun ġentili biżżejjed biex tuża softwer minn Adobe stess - Adobe Media Server (AMS).
Wara xi żmien fl-2012, Adobe "ċediet u beżgħet" lill-pubbliku. Protokoll RTMP, li kien fih żbalji u kien essenzjalment mhux komplut. Sa dak iż-żmien, l-iżviluppaturi bdew jagħmlu l-implimentazzjonijiet tagħhom stess ta 'dan il-protokoll, u s-server Wowza deher. Fl-2011, Adobe ressqet kawża kontra Wowza għal użu illegali ta 'privattivi relatati mal-RTMP wara 4 snin, il-kunflitt ġie solvut b'mod amikevoli;
Il-pjattaforma Adobe Flash għandha aktar minn 20 sena, li matulu ġew skoperti ħafna vulnerabbiltajiet kritiċi, appoġġ biex jintemm sal-2020, u jħalli ftit alternattivi għas-servizz ta' streaming.
Għall-proġett tiegħi, immedjatament iddeċidejt li nabbanduna kompletament l-użu ta 'Flash fil-browser. Jien indikajt ir-raġuni ewlenija hawn fuq; Flash lanqas mhu appoġġjat xejn fuq pjattaformi mobbli, u verament ma ridtx niskjera Adobe Flash għall-iżvilupp fuq Windows (emulatur tal-inbid). Allura bdejt nikteb klijent f'JavaScript. Dan se jkun biss prototip, peress li aktar tard tgħallimt li l-istreaming jista 'jsir b'mod ħafna aktar effiċjenti bbażat fuq p2p, għalija biss se jkun peer - server - peers, iżda aktar dwar dan darba oħra, għax għadu mhux lest.
Biex tibda, għandna bżonn is-server tal-websockets attwali. Għamilt l-aktar waħda sempliċi bbażata fuq il-pakkett tal-melody go:
Kodiċi tas-server
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)
}
Fuq il-klijent (in-naħa tal-istreaming), l-ewwel trid taċċessa l-kamera. Dan isir permezz .
Aħna niksbu aċċess (permess) għall-kamera/mikrofonu permezz . Din l-API tipprovdi metodu , li turi popup. tieqa li titlob lill-utent il-permess biex jaċċessa l-kamera u/jew il-mikrofonu. Nixtieq ninnota li wettaqt l-esperimenti kollha fil-Google Chrome, iżda naħseb li kollox se jaħdem dwar l-istess fil-Firefox.
Sussegwentement, getUserMedia() jirritorna Wegħda, li lilha tgħaddi oġġett MediaStream - fluss ta 'dejta tal-vidjo-awdjo. Aħna jassenjaw dan l-oġġett għall-proprjetà src tal-element tal-vidjo. Kodiċi:
In-naħa tax-xandir
<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>
Biex tixxandar nixxiegħa tal-vidjow fuq sokits, għandek bżonn tikkodifikah x'imkien, taffah, u tittrasmettih f'partijiet. Il-fluss tal-vidjo mhux ipproċessat ma jistax jiġi trażmess permezz ta 'websockets. Dan huwa fejn niġu għall-għajnuna tagħna . Din l-API tippermettilek tikkodifika u tkisser il-fluss f'biċċiet. Nagħmel kodifikazzjoni biex tikkompressa l-fluss tal-vidjo sabiex nibgħat inqas bytes fuq in-netwerk. Wara li qatgħetha f'biċċiet, tista 'tibgħat kull biċċa lil websocket. Kodiċi:
Aħna nikkodifikaw il-fluss tal-vidjo, nkissruh f'biċċiet
<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>
Issa ejja nżidu t-trasmissjoni permezz ta 'websockets. B'mod sorprendenti, kulma għandek bżonn għal dan huwa oġġett . Għandha biss żewġ metodi tibgħat u tagħlaq. L-ismijiet jitkellmu waħedhom. Kodiċi miżjud:
Aħna jittrasmettu l-fluss tal-vidjo lis-server
<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>
In-naħa tax-xandir hija lesta! Issa ejja nippruvaw nirċievu nixxiegħa tal-vidjo u juruha fuq il-klijent. X'għandna bżonn għal dan? L-ewwelnett, ovvjament, il-konnessjoni tas-sokit. Aħna nehmeż "semmiegħ" mal-oġġett WebSocket u tabbona għall-avveniment 'messaġġ'. Wara li rċieva biċċa dejta binarja, is-server tagħna jxandarha lill-abbonati, jiġifieri, lill-klijenti. F'dan il-każ, il-funzjoni ta 'callback assoċjata mas-"semmiegħ" tal-avveniment 'messaġġ' tiġi attivata fuq il-klijent l-oġġett innifsu jiġi mgħoddi fl-argument tal-funzjoni - biċċa tal-fluss tal-vidjo kodifikat minn vp8;
Aħna naċċettaw nixxiegħa tal-vidjo
<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>
Għal żmien twil ippruvajt nifhem għaliex huwa impossibbli li tibgħat immedjatament il-biċċiet riċevuti lill-element tal-vidjo għall-plejbek, iżda rriżulta li dan ma jistax isir, ovvjament, l-ewwel trid tpoġġi l-biċċa f'buffer speċjali marbut ma ' l-element tal-vidjo, u mbagħad biss jibda jilgħab il-fluss tal-vidjo. Għal dan ser ikollok bżonn и .
MediaSource jaġixxi bħala tip ta 'intermedjarju bejn l-oġġett tal-qari tal-midja u s-sors ta' dan il-fluss tal-midja. L-oġġett MediaSource fih buffer pluggable għas-sors tal-fluss tal-vidjo/awdjo. Karatteristika waħda hija li l-buffer jista 'jżomm biss data Uint8, għalhekk ser ikollok bżonn FileReader biex toħloq tali buffer. Ħares lejn il-kodiċi u jsir aktar ċar:
Jilgħab il-fluss tal-vidjo
<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>
Il-prototip tas-servizz ta' streaming huwa lest. L-iżvantaġġ ewlieni huwa li d-daqq tal-vidjo se jibqa 'wara n-naħa li tittrasmetti b'100 ms aħna nissettjaw dan aħna stess meta naqsmu l-fluss tal-vidjo qabel ma jittrasmettuh lis-server; Barra minn hekk, meta ċċekkjajt fuq il-laptop tiegħi, id-dewmien bejn in-naħat li jittrasmetti u li jirċievi gradwalment akkumula, dan kien viżibbli b'mod ċar. Bdejt infittex modi kif negħleb dan l-iżvantaġġ, u... sab , li jippermettilek tittrasmetti nixxiegħa tal-vidjo mingħajr tricks bħall-qsim tal-fluss f'biċċiet. Id-dewmien li jakkumula, naħseb, huwa dovut għall-fatt li l-browser jerġa 'kodifika kull biċċa fil-format webm qabel it-trażmissjoni. Ma ħafferx aktar, iżda bdejt nistudja WebRTC naħseb li nikteb artiklu separat dwar ir-riżultati tar-riċerka tiegħi jekk insibha interessanti għall-komunità.
Sors: www.habr.com
