(ከሞላ ጎደል) ከንቱ የድር ካሜራ ከአሳሽ መልቀቅ። የሚዲያ ዥረት እና ዌብሶኬቶች

በዚህ ጽሑፍ ውስጥ እንደ አዶቤ ፍላሽ ማጫወቻ ያሉ የሶስተኛ ወገን አሳሽ ተሰኪዎችን ሳልጠቀም ቪዲዮን በዌብሶኬቶች ለማሰራጨት ያደረግኩትን ሙከራ ማካፈል እፈልጋለሁ። ምን መጣ ፣ አንብብ።

አዶቤ ፍላሽ - ቀደም ሲል ማክሮሚዲያ ፍላሽ በድር አሳሽ ውስጥ የሚሰሩ መተግበሪያዎችን ለመፍጠር የሚያስችል መድረክ ነው። የሚዲያ ዥረት ኤፒአይ ከመጀመሩ በፊት ከድር ካሜራ ቪዲዮ እና ድምጽ ለማሰራጨት እንዲሁም በአሳሹ ውስጥ የተለያዩ ኮንፈረንስ እና ቻቶች ለመፍጠር ብቸኛው መድረክ ነበር ። የሚዲያ መረጃን የማስተላለፊያ ፕሮቶኮል RTMP (Real Time Messaging Protocol) በትክክል ለረጅም ጊዜ ተዘግቷል ይህም ማለት፡ የዥረት አገልግሎትዎን ከፍ ለማድረግ ከፈለጉ እባኮትን ሶፍትዌር ከራሳቸው አዶቤ ይጠቀሙ - አዶቤ ሚዲያ አገልጋይ (ኤኤምኤስ)።

እ.ኤ.አ. በ2012 ከተወሰነ ጊዜ በኋላ አዶቤ ለህዝብ “ተወ እና ተፉ ዝርዝር መግለጫ ስህተቶችን የያዘ እና በመሠረቱ ያልተሟላ የ RTMP ፕሮቶኮል በዚያን ጊዜ ገንቢዎቹ የዚህን ፕሮቶኮል የራሳቸውን ተግባራዊ ማድረግ ጀመሩ, ስለዚህ Wowza አገልጋይ ታየ. እ.ኤ.አ. በ 2011 አዶቤ ከ 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.

ወደ ካሜራ/ማይክራፎን መዳረሻ (ፍቃድ) እናገኛለን የሚዲያ መሳሪያዎች ኤፒአይ. ይህ ኤፒአይ ዘዴን ያቀርባል MediaDevices.getUserMedia(), ይህም ፖፕ ያሳያል. ካሜራውን እና/ወይም ማይክሮፎኑን ለመድረስ ተጠቃሚው ፍቃድ የሚጠይቅ መስኮት። በ Google Chrome ውስጥ ሁሉንም ሙከራዎች እንዳካሄድኩ ማስተዋል እፈልጋለሁ, ነገር ግን ሁሉም ነገር በፋየርፎክስ ውስጥ በተመሳሳይ መንገድ እንደሚሰራ አስባለሁ.

በመቀጠል 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>

የቪዲዮ ዥረትን በሶኬቶች በኩል ለማሰራጨት የሆነ ቦታ ላይ ኢንኮድ ማድረግ፣ ማቆየት እና በክፍሎች ማስተላለፍ ያስፈልጋል። ጥሬው የቪዲዮ ዥረቱ በዌብሶኬቶች ሊተላለፍ አይችልም። እዚህ እኛን ለመርዳት መጥቷል የሚዲያ መቅጃ ኤፒአይ. ይህ ኤፒአይ ዥረቱን በኮድ እንዲያደርጉ እና እንዲከፍሉ ይፈቅድልዎታል። በአውታረ መረቡ ላይ ጥቂት ባይት ለማሽከርከር የቪዲዮ ዥረቱን ለመጭመቅ ኢንኮዲንግ አደርጋለሁ። ከተከፋፈሉ በኋላ እያንዳንዱን ቁራጭ ወደ ዌብሶኬት መላክ ይችላሉ። ኮድ፡-

የቪዲዮ ዥረቱን በኮድ እናስቀምጠዋለን, ወደ ክፍሎች እንመታዋለን

<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 ነገር ላይ "አድማጭ" (አድማጭ) አንጠልጥለናል፣ ለ'መልዕክት' ክስተት ደንበኝነት ይመዝገቡ። ሁለትዮሽ ውሂብ ከተቀበልን አገልጋያችን ለተመዝጋቢዎቹ ማለትም ለደንበኞቻችን ያሰራጫል። በተመሳሳይ ጊዜ, ከ "መልእክት" ክስተት "አድማጭ" ጋር የተያያዘው የመልሶ መደወል ተግባር በደንበኛው ላይ ተቀስቅሷል, እቃው ራሱ ወደ ተግባር ነጋሪ እሴት ይተላለፋል - የቪዲዮ ዥረት ቁራጭ በ 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 ማጥናት ጀመርኩ፣ ስለ ምርምሬ ውጤቶች አስባለሁ፣ ለህብረተሰቡ አስደሳች ሆኖ ካገኘሁት የተለየ ጽሑፍ እጽፋለሁ።

ምንጭ: hab.com

አስተያየት ያክሉ