Π ΡΡΠ°ΡΡΠ΅ Ρ Ρ
ΠΎΡΡ ΠΏΠΎΠ΄Π΅Π»ΠΈΡΡΡΡ ΡΠ²ΠΎΠΈΠΌΠΈ ΠΏΠΎΠΏΡΡΠΊΠ°ΠΌΠΈ ΡΠ΄Π΅Π»Π°ΡΡ ΡΡΡΠΈΠΌΠΌΠΈΠ½Π³ Π²ΠΈΠ΄Π΅ΠΎ ΡΠ΅ΡΠ΅Π· websockets Π±Π΅Π· ΠΈΡΠΏΠΎΠ»ΡΠ·ΠΎΠ²Π°Π½ΠΈΡ ΡΡΠΎΡΠΎΠ½Π½ΠΈΡ
ΠΏΠ»Π°Π³ΠΈΠ½ΠΎΠ² Π±ΡΠ°ΡΠ·Π΅ΡΠ° ΡΠΈΠΏΠ° Adobe Flash Player. Π§ΡΠΎ ΠΈΠ· ΡΡΠΎΠ³ΠΎ ΠΏΠΎΠ»ΡΡΠΈΠ»ΠΎΡΡ ΡΠΈΡΠ°ΠΉΡΠ΅ Π΄Π°Π»Π΅Π΅.
Adobe Flash β ΡΠ°Π½Π΅Π΅ Macromedia Flash, ΡΡΠΎ ΠΏΠ»Π°ΡΡΠΎΡΠΌΠ° Π΄Π»Ρ ΡΠΎΠ·Π΄Π°Π½ΠΈΡ ΠΏΡΠΈΠ»ΠΎΠΆΠ΅Π½ΠΈΠΉ, ΡΠ°Π±ΠΎΡΠ°ΡΡΠΈΡ
Π² Π²Π΅Π±-Π±ΡΠ°ΡΠ·Π΅ΡΠ΅. ΠΠΎ Π²Π½Π΅Π΄ΡΠ΅Π½ΠΈΡ Media Stream API ΡΡΠΎ Π±ΡΠ»Π° ΠΏΡΠ°ΠΊΡΠΈΡΠ΅ΡΠΊΠΈ Π΅Π΄ΠΈΠ½ΡΡΠ²Π΅Π½Π½Π°Ρ ΠΏΠ»Π°ΡΡΠΎΡΠΌΠ° Π΄Π»Ρ ΡΡΡΠΈΠΌΠΌΠΈΠ½Π³Π° Ρ Π²Π΅Π±-ΠΊΠ°ΠΌΠ΅ΡΡ Π²ΠΈΠ΄Π΅ΠΎ ΠΈ Π³ΠΎΠ»ΠΎΡΠ°, Π° ΡΠ°ΠΊΠΆΠ΅ Π΄Π»Ρ ΡΠΎΠ·Π΄Π°Π½ΠΈΡ ΡΠ°Π·Π»ΠΈΡΠ½ΠΎΠ³ΠΎ ΡΠΎΠ΄Π° ΠΊΠΎΠ½ΡΠ΅ΡΠ΅Π½ΡΠΈΠΉ ΠΈ ΡΠ°ΡΠΎΠ² Π² Π±ΡΠ°ΡΠ·Π΅ΡΠ΅. ΠΡΠΎΡΠΎΠΊΠΎΠ» Π΄Π»Ρ ΠΏΠ΅ΡΠ΅Π΄Π°ΡΠΈ ΠΌΠ΅Π΄ΠΈΠ°-ΠΈΠ½ΡΠΎΡΠΌΠ°ΡΠΈΠΈ RTMP (Real Time Messaging Protocol), Π±ΡΠ» ΡΠ°ΠΊΡΠΈΡΠ΅ΡΠΊΠΈ Π·Π°ΠΊΡΡΡΡΠΌ Π΄ΠΎΠ»Π³ΠΎΠ΅ Π²ΡΠ΅ΠΌΡ, ΡΡΠΎ ΠΎΠ·Π½Π°ΡΠ°Π»ΠΎ: Π΅ΡΠ»ΠΈ Ρ
ΠΎΡΠ΅ΡΡ ΠΏΠΎΠ΄Π½ΡΡΡ ΡΠ²ΠΎΠΉ ΡΡΡΠΈΠΌΠΌΠΈΠ½Π³-ΡΠ΅ΡΠ²ΠΈΡ, Π±ΡΠ΄Ρ Π΄ΠΎΠ±Ρ ΠΈΡΠΏΠΎΠ»ΡΠ·ΡΠΉ ΡΠΎΡΡ ΠΎΡ ΡΠ°ΠΌΠΈΡ
Adobe β Adobe Media Server (AMS).
Π§Π΅ΡΠ΅Π· Π½Π΅ΠΊΠΎΡΠΎΡΠΎΠ΅ Π²ΡΠ΅ΠΌΡ Π² 2012 Adobe Β«ΡΠ΄Π°Π»ΠΈΡΡ ΠΈ Π²ΡΠΏΠ»ΡΠ½ΡΠ»ΠΈΒ» Π½Π° ΡΡΠ΄ ΠΏΡΠ±Π»ΠΈΠΊΠΈ
Adobe Flash ΠΏΠ»Π°ΡΡΠΎΡΠΌΠ΅ ΡΠΆΠ΅ Π±ΠΎΠ»ΡΡΠ΅ 20 Π»Π΅Ρ, Π·Π° ΡΡΠΎ Π²ΡΠ΅ΠΌΡ ΠΎΠ±Π½Π°ΡΡΠΆΠΈΠ»ΠΎΡΡ ΠΌΠ½ΠΎΠΆΠ΅ΡΡΠ²ΠΎ ΠΊΡΠΈΡΠΈΡΠ΅ΡΠΊΠΈΡ
ΡΡΠ·Π²ΠΈΠΌΠΎΡΡΠ΅ΠΉ, ΠΏΠΎΠ΄Π΄Π΅ΡΠΆΠΊΡ
ΠΠ»Ρ ΡΠ²ΠΎΠ΅Π³ΠΎ ΠΏΡΠΎΠ΅ΠΊΡΠ° Ρ ΡΡΠ°Π·Ρ ΡΠ΅ΡΠΈΠ» ΠΏΠΎΠ»Π½ΠΎΡΡΡΡ ΠΎΡΠΊΠ°Π·Π°ΡΡΡΡ ΠΎΡ ΠΈΡΠΏΠΎΠ»ΡΠ·ΠΎΠ²Π°Π½ΠΈΡ Flash Π² Π±ΡΠ°ΡΠ·Π΅ΡΠ΅. ΠΡΠ½ΠΎΠ²Π½ΡΡ ΠΏΡΠΈΡΠΈΠ½Ρ Ρ ΡΠΊΠ°Π·Π°Π» Π²ΡΡΠ΅, ΡΠ°ΠΊΠΆΠ΅ Flash ΡΠΎΠ²ΡΠ΅ΠΌ Π½Π΅ ΠΏΠΎΠ΄Π΄Π΅ΡΠΆΠΈΠ²Π°Π΅ΡΡΡ Π½Π° ΠΌΠΎΠ±ΠΈΠ»ΡΠ½ΡΡ ΠΏΠ»Π°ΡΡΠΎΡΠΌΠ°Ρ , Π΄Π° ΠΈ ΡΠ°Π·Π²ΠΎΡΠ°ΡΠΈΠ²Π°ΡΡ Adobe Flash Π΄Π»Ρ ΡΠ°Π·ΡΠ°Π±ΠΎΡΠΊΠΈ Π½Π° windows (ΡΠΌΡΠ»ΡΡΠΎΡΠ΅ wine) ΡΠΎΠ²ΡΠ΅ΠΌ ΡΠΆ Π½Π΅ Ρ ΠΎΡΠ΅Π»ΠΎΡΡ. ΠΠΎΡΡΠΎΠΌΡ Ρ Π²Π·ΡΠ»ΡΡ ΠΏΠΈΡΠ°ΡΡ ΠΊΠ»ΠΈΠ΅Π½Ρ Π½Π° JavaScript. ΠΡΠΎ Π±ΡΠ΄Π΅Ρ Π²ΡΠ΅Π³ΠΎ Π»ΠΈΡΡ ΠΏΡΠΎΡΠΎΡΠΈΠΏ, ΡΠ°ΠΊ ΠΊΠ°ΠΊ Π² Π΄Π°Π»ΡΠ½Π΅ΠΉΡΠ΅ΠΌ Ρ ΡΠ·Π½Π°Π», ΡΡΠΎ ΡΡΡΠΈΠΌΠΈΠ½Π³ ΠΌΠΎΠΆΠ½ΠΎ ΡΠ΄Π΅Π»Π°ΡΡ Π³ΠΎΡΠ°Π·Π΄ΠΎ ΡΡΡΠ΅ΠΊΡΠΈΠ²Π½Π΅Π΅ Π½Π° ΠΎΡΠ½ΠΎΠ²Π΅ p2p, ΡΠΎΠ»ΡΠΊΠΎ Ρ ΠΌΠ΅Π½Ρ ΡΡΠΎ Π±ΡΠ΄Π΅Ρ peer β server β peers, Π½ΠΎ ΠΎΠ± ΡΡΠΎΠΌ Π² Π΄ΡΡΠ³ΠΎΠΉ ΡΠ°Π·, ΠΏΠΎΡΠΎΠΌΡ ΡΡΠΎ ΡΡΠΎ Π΅ΡΠ΅ Π½Π΅ Π³ΠΎΡΠΎΠ²ΠΎ.
ΠΠ»Ρ Π½Π°ΡΠ°Π»Π° ΡΠ°Π±ΠΎΡΡ Π½Π°ΠΌ Π½Π΅ΠΎΠ±Ρ ΠΎΠ΄ΠΈΠΌ ΡΠΎΠ±ΡΡΠ²Π΅Π½Π½ΠΎ websockets-ΡΠ΅ΡΠ²Π΅Ρ. Π― ΡΠ΄Π΅Π»Π°Π» ΠΏΡΠΎΡΡΠ΅ΠΉΡΠΈΠΉ Π½Π° ΠΎΡΠ½ΠΎΠ²Π΅ go-ΠΏΠ°ΠΊΠ΅ΡΠ° melody:
ΠΠΎΠ΄ ΡΠ΅ΡΠ²Π΅ΡΠ½ΠΎΠΉ ΡΠ°ΡΡΠΈ
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)
}
ΠΠ° ΠΊΠ»ΠΈΠ΅Π½ΡΠ΅ (ΡΡΠ°Π½ΡΠ»ΠΈΡΡΡΡΠ΅ΠΉ ΡΡΠΎΡΠΎΠ½Π΅) ΡΠ½Π°ΡΠ°Π»Π° Π½Π΅ΠΎΠ±Ρ
ΠΎΠ΄ΠΈΠΌΠΎ ΠΏΠΎΠ»ΡΡΠΈΡΡ Π΄ΠΎΡΡΡΠΏ ΠΊ ΠΊΠ°ΠΌΠ΅ΡΠ΅. ΠΠ΅Π»Π°Π΅ΡΡΡ ΡΡΠΎ ΡΠ΅ΡΠ΅Π·
ΠΠΎΠ»ΡΡΠ°Π΅ΠΌ Π΄ΠΎΡΡΡΠΏ (ΡΠ°Π·ΡΠ΅ΡΠ΅Π½ΠΈΠ΅) ΠΊ ΠΊΠ°ΠΌΠ΅ΡΠ΅/ΠΌΠΈΠΊΡΠΎΡΠΎΠ½Ρ ΡΠ΅ΡΠ΅Π·
ΠΠ°Π»Π΅Π΅ getUserMedia() Π²ΠΎΠ·Π²ΡΠ°ΡΠ°Π΅Ρ Promise, Π² ΠΊΠΎΡΠΎΡΠΎΠ΅ ΠΏΠ΅ΡΠ΅Π΄Π°Π΅Ρ MediaStream ΠΎΠ±ΡΠ΅ΠΊΡ β ΠΏΠΎΡΠΎΠΊ Π²ΠΈΠ΄Π΅ΠΎ-Π°ΡΠ΄ΠΈΠΎ Π΄Π°Π½Π½ΡΡ . ΠΡΠΎΡ ΠΎΠ±ΡΠ΅ΠΊΡ ΠΌΡ ΠΏΡΠΈΡΠ²Π°ΠΈΠ²Π°Π΅ΠΌ Π² src ΡΠ²ΠΎΠΉΡΡΠ²ΠΎ ΡΠ»Π΅ΠΌΠ΅Π½ΡΠ° 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');
// Π΅ΡΠ»ΠΈ Π΄ΠΎΡΡΡΠΏΠ΅Π½ MediaDevices API, ΠΏΡΡΠ°Π΅ΠΌΡΡ ΠΏΠΎΠ»ΡΡΠΈΡΡ Π΄ΠΎΡΡΡΠΏ ΠΊ ΠΊΠ°ΠΌΠ΅ΡΠ΅ (ΠΌΠΎΠΆΠ½ΠΎ Π΅ΡΠ΅ ΠΈ ΠΊ ΠΌΠΈΠΊΡΠΎΡΠΎΠ½Ρ)
// getUserMedia Π²Π΅ΡΠ½Π΅Ρ ΠΎΠ±Π΅ΡΠ°Π½ΠΈΠ΅, Π½Π° ΠΊΠΎΡΠΎΡΠΎΠ΅ ΠΏΠΎΠ΄ΠΏΠΈΡΡΠ²Π°Π΅ΠΌΡΡ ΠΈ ΠΏΠΎΠ»ΡΡΠ΅Π½Π½ΡΠΉ Π²ΠΈΠ΄Π΅ΠΎΠΏΠΎΡΠΎΠΊ Π² ΠΊΠΎΠ»Π±Π΅ΠΊΠ΅ Π½Π°ΠΏΡΠ°Π²Π»ΡΠ΅ΠΌ Π² video ΠΎΠ±ΡΠ΅ΠΊΡ Π½Π° ΡΡΡΠ°Π½ΠΈΡΠ΅
if (navigator.mediaDevices.getUserMedia) {
navigator.mediaDevices.getUserMedia({video: true}).then(function (stream) {
// Π²ΠΈΠ΄Π΅ΠΎ ΠΏΠΎΡΠΎΠΊ ΠΏΡΠΈΠ²ΡΠ·ΡΠ²Π°Π΅ΠΌ ΠΊ video ΡΠ΅Π³Ρ, ΡΡΠΎΠ±Ρ ΠΊΠ»ΠΈΠ΅Π½Ρ ΠΌΠΎΠ³ Π²ΠΈΠ΄Π΅ΡΡ ΡΠ΅Π±Ρ ΠΈ ΠΊΠΎΠ½ΡΡΠΎΠ»ΠΈΡΠΎΠ²Π°ΡΡ
video.srcObject = stream;
});
}
</script>
Π§ΡΠΎΠ±Ρ ΡΡΠ°Π½ΡΠ»ΠΈΡΠΎΠ²Π°ΡΡ Π²ΠΈΠ΄Π΅ΠΎΠΏΠΎΡΠΎΠΊ ΡΠ΅ΡΠ΅Π· ΡΠΎΠΊΠ΅ΡΡ, Π½Π΅ΠΎΠ±Ρ
ΠΎΠ΄ΠΈΠΌΠΎ Π΅Π³ΠΎ ΠΊΠ°ΠΊ-ΡΠΎ Π³Π΄Π΅-ΡΠΎ ΠΊΠΎΠ΄ΠΈΡΠΎΠ²Π°ΡΡ, Π±ΡΡΠ΅ΡΠΈΠ·ΠΈΡΠΎΠ²Π°ΡΡ, ΠΈ ΠΏΠ΅ΡΠ΅Π΄Π°Π²Π°ΡΡ ΡΠ°ΡΡΡΠΌΠΈ. Π‘ΡΡΠΎΠΉ Π²ΠΈΠ΄Π΅ΠΎΠΏΠΎΡΠΎΠΊ Π½Π΅ ΠΏΠ΅ΡΠ΅Π΄Π°ΡΡ ΡΠ΅ΡΠ΅Π· websockets. Π’ΡΡ Π½Π° ΠΏΠΎΠΌΠΎΡΡ Π½Π°ΠΌ ΠΏΡΠΈΡ
ΠΎΠ΄ΠΈΡ
ΠΠΎΠ΄ΠΈΡΡΠ΅ΠΌ Π²ΠΈΠ΄Π΅ΠΎΠΏΠΎΡΠΎΠΊ, Π±ΡΠ΅ΠΌ Π΅Π³ΠΎ Π½Π° ΡΠ°ΡΡΠΈ
<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>
Π’Π΅ΠΏΠ΅ΡΡ Π΄ΠΎΠ±Π°Π²ΠΈΠΌ ΠΏΠ΅ΡΠ΅Π΄Π°ΡΡ ΠΏΠΎ websockets. ΠΠ°ΠΊ Π½ΠΈ ΡΠ΄ΠΈΠ²ΠΈΡΠ΅Π»ΡΠ½ΠΎ, Π΄Π»Ρ ΡΡΠΎΠ³ΠΎ Π½ΡΠΆΠ΅Π½ Π»ΠΈΡΡ ΠΎΠ±ΡΠ΅ΠΊΡ
ΠΠ΅ΡΠ΅Π΄Π°Π΅ΠΌ Π²ΠΈΠ΄Π΅ΠΎΠΏΠΎΡΠΎΠΊ Π½Π° ΡΠ΅ΡΠ²Π΅Ρ
<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 Π²Π΅ΡΠ°Π΅ΠΌ Β«ΡΠ»ΡΡΠ°ΡΠ΅Π»ΡΒ» (listener), ΠΏΠΎΠ΄ΠΏΠΈΡΡΠ²Π°Π΅ΠΌΡΡ Π½Π° ΡΠΎΠ±ΡΡΠΈΠ΅ ‘message’. ΠΠΎΠ»ΡΡΠΈΠ² ΠΊΡΡΠΎΡΠ΅ΠΊ Π±ΠΈΠ½Π°ΡΠ½ΡΡ Π΄Π°Π½Π½ΡΡ Π½Π°Ρ ΡΠ΅ΡΠ²Π΅Ρ Π±ΡΠΎΠ΄ΠΊΠ°ΡΡΠΈΡ Π΅Π³ΠΎ ΠΏΠΎΠ΄ΠΏΠΈΡΡΠΈΠΊΠ°ΠΌ, ΡΠΎ Π΅ΡΡΡ ΠΊΠ»ΠΈΠ΅Π½ΡΠ°ΠΌ. ΠΠ° ΠΊΠ»ΠΈΠ΅Π½ΡΠ΅ ΠΏΡΠΈ ΡΡΠΎΠΌ ΡΡΠ°Π±Π°ΡΡΠ²Π°Π΅Ρ callback-ΡΡΠ½ΠΊΡΠΈΡ ΡΠ²ΡΠ·Π°Π½Π½Π°Ρ Ρ Β«ΡΠ»ΡΡΠ°ΡΠ΅Π»Π΅ΠΌΒ» ΡΠΎΠ±ΡΡΠΈΡ ‘message’, Π² Π°ΡΠ³ΡΠΌΠ΅Π½Ρ ΡΡΠ½ΠΊΡΠΈΠΈ ΠΏΠ΅ΡΠ΅Π΄Π°Π΅ΡΡΡ ΡΠΎΠ±ΡΡΠ²Π΅Π½Π½ΠΎ ΡΠ°ΠΌ ΠΎΠ±ΡΠ΅ΠΊΡ β ΠΊΡΡΠΎΡΠ΅ΠΊ Π²ΠΈΠ΄Π΅ΠΎΠΏΠΎΡΠΎΠΊΠ°, Π·Π°ΠΊΠΎΠ΄ΠΈΡΠΎΠ²Π°Π½Π½ΡΠΉ 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>
Π― Π΄ΠΎΠ»Π³ΠΎΠ΅ Π²ΡΠ΅ΠΌΡ ΠΏΡΡΠ°Π»ΡΡ ΠΏΠΎΠ½ΡΡΡ, ΠΏΠΎΡΠ΅ΠΌΡ Π½Π΅Π»ΡΠ·Ρ ΠΏΠΎΠ»ΡΡΠ΅Π½Π½ΡΠ΅ ΠΊΡΡΠΎΡΠΊΠΈ ΡΡΠ°Π·Ρ ΠΆΠ΅ ΠΎΡΠΏΡΠ°Π²ΠΈΡΡ Π½Π° Π²ΠΎΡΠΏΡΠΎΠΈΠ·Π²Π΅Π΄Π΅Π½ΠΈΠ΅ ΡΠ»Π΅ΠΌΠ΅Π½ΡΡ video, Π½ΠΎ ΠΎΠΊΠ°Π·Π°Π»ΠΎΡΡ ΡΠ°ΠΊ ΠΊΠΎΠ½Π΅ΡΠ½ΠΎ Π½Π΅Π»ΡΠ·Ρ Π΄Π΅Π»Π°ΡΡ, Π½ΡΠΆΠ½ΠΎ ΡΠ½Π°ΡΠ°Π»Π° ΠΊΡΡΠΎΡΠ΅ΠΊ ΠΏΠΎΠ»ΠΎΠΆΠΈΡΡ Π² ΡΠΏΠ΅ΡΠΈΠ°Π»ΡΠ½ΡΠΉ Π±ΡΡΠ΅Ρ, ΠΏΡΠΈΠ²ΡΠ·Π°Π½Π½ΡΠΉ ΠΊ ΡΠ»Π΅ΠΌΠ΅Π½ΡΡ video, ΠΈ ΡΠΎΠ»ΡΠΊΠΎ ΡΠΎΠ³Π΄Π° Π½Π°ΡΠ½Π΅Ρ Π²ΠΎΡΠΏΡΠΎΠΈΠ·Π²ΠΎΠ΄ΠΈΡΡ Π²ΠΈΠ΄Π΅ΠΎΠΏΠΎΡΠΎΠΊ. ΠΠ»Ρ ΡΡΠΎΠ³ΠΎ ΠΏΠΎΠ½Π°Π΄ΠΎΠ±ΠΈΡΡΡ
MediaSource Π²ΡΡΡΡΠΏΠ°Π΅Ρ Π½Π΅ΠΊΠΈΠΌ ΠΏΠΎΡΡΠ΅Π΄Π½ΠΈΠΊΠΎΠΌ ΠΌΠ΅ΠΆΠ΄Ρ ΠΎΠ±ΡΠ΅ΠΊΡΠΎΠΌ Π²ΠΎΡΠΏΡΠΎΠΈΠ·Π²Π΅Π΄Π΅Π½ΠΈΡ media ΠΈ ΠΈΡΡΠΎΡΠ½ΠΈΠΊΠΎΠΌ Π΄Π°Π½Π½ΠΎΠ³ΠΎ ΠΏΠΎΡΠΎΠΊΠ° ΠΌΠ΅Π΄ΠΈΠ°. 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 ΠΌΡ, ΡΡΠΎ ΠΌΡ Π·Π°Π΄Π°Π»ΠΈ ΡΠ°ΠΌΠΈ ΠΏΡΠΈ ΡΠ°Π·Π±ΠΈΠ΅Π½ΠΈΠΈ Π²ΠΈΠ΄Π΅ΠΎΠΏΠΎΡΠΎΠΊΠ° ΠΏΠ΅ΡΠ΅Π΄ ΠΏΠ΅ΡΠ΅Π΄Π°ΡΠ΅ΠΉ Π½Π° ΡΠ΅ΡΠ²Π΅Ρ. ΠΠΎΠ»Π΅Π΅ ΡΠΎΠ³ΠΎ, ΠΊΠΎΠ³Π΄Π° Ρ ΠΏΡΠΎΠ²Π΅ΡΡΠ» Ρ ΡΠ΅Π±Ρ Π½Π° Π½ΠΎΡΡΠ±ΡΠΊΠ΅, Ρ ΠΌΠ΅Π½Ρ ΠΏΠΎΡΡΠ΅ΠΏΠ΅Π½Π½ΠΎ ΠΊΠΎΠΏΠΈΠ»ΡΡ Π»Π°Π³ ΠΌΠ΅ΠΆΠ΄Ρ ΠΏΠ΅ΡΠ΅Π΄Π°ΡΡΠ΅ΠΉ ΠΈ ΠΏΡΠΈΠ½ΠΈΠΌΠ°ΡΡΠ΅ΠΉ ΡΡΠΎΡΠΎΠ½ΠΎΠΉ, ΡΡΠΎ Π±ΡΠ»ΠΎ Ρ
ΠΎΡΠΎΡΠΎ Π²ΠΈΠ΄Π½ΠΎ. Π― Π½Π°ΡΠ°Π» ΠΈΡΠΊΠ°ΡΡ ΡΠΏΠΎΡΠΎΠ±Ρ ΠΊΠ°ΠΊ ΠΏΠΎΠ±ΠΎΡΠΎΡΡ Π΄Π°Π½Π½ΡΠΉ Π½Π΅Π΄ΠΎΡΡΠ°ΡΠΎΠΊ, ΠΈβ¦ Π½Π°Π±ΡΠ΅Π» Π½Π°
ΠΡΡΠΎΡΠ½ΠΈΠΊ: habr.com