اس آرٹیکل میں میں ویب ساکٹ کے ذریعے ویڈیو کو اسٹریم کرنے کی اپنی کوششوں کو شیئر کرنا چاہتا ہوں بغیر تھرڈ پارٹی براؤزر پلگ ان جیسے کہ ایڈوب فلیش پلیئر۔ اس سے کیا نکلا یہ جاننے کے لیے پڑھیں۔
ایڈوب فلیش، پہلے میکرومیڈیا فلیش، ویب براؤزر میں چلنے والی ایپلی کیشنز بنانے کا ایک پلیٹ فارم ہے۔ میڈیا اسٹریم API کے متعارف ہونے سے پہلے، یہ عملی طور پر ویب کیم سے ویڈیو اور آواز کو اسٹریم کرنے کے ساتھ ساتھ براؤزر میں مختلف قسم کی کانفرنسیں اور چیٹس بنانے کا واحد پلیٹ فارم تھا۔ میڈیا کی معلومات کی ترسیل کے لیے پروٹوکول RTMP (Real Time Messaging Protocol) دراصل ایک طویل عرصے کے لیے بند تھا، جس کا مطلب تھا: اگر آپ اپنی اسٹریمنگ سروس کو بڑھانا چاہتے ہیں، تو خود ایڈوب سے سافٹ ویئر استعمال کرنے کے لیے مہربان رہیں - Adobe Media Server (AMS)۔
2012 میں کچھ عرصے کے بعد، Adobe نے عوام کے سامنے "ہت ہار دی اور اسے تھوک دیا۔"
Adobe Flash پلیٹ فارم 20 سال سے زیادہ پرانا ہے، اس دوران کئی اہم کمزوریاں دریافت ہوئیں، سپورٹ
اپنے پروجیکٹ کے لیے، میں نے فوری طور پر براؤزر میں فلیش کے استعمال کو مکمل طور پر ترک کرنے کا فیصلہ کیا۔ میں نے اوپر کی بنیادی وجہ کی نشاندہی کی؛ موبائل پلیٹ فارمز پر فلیش بھی بالکل بھی تعاون یافتہ نہیں ہے، اور میں واقعی میں ونڈوز (وائن ایمولیٹر) پر ترقی کے لیے ایڈوب فلیش کو تعینات نہیں کرنا چاہتا تھا۔ تو میں جاوا اسکرپٹ میں کلائنٹ لکھنے کے لیے نکلا۔ یہ صرف ایک پروٹو ٹائپ ہو گا، کیونکہ بعد میں میں نے سیکھا کہ 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)
}
کلائنٹ (سٹریمنگ سائیڈ) پر، آپ کو پہلے کیمرے تک رسائی حاصل کرنے کی ضرورت ہے۔ اس کے ذریعے کیا جاتا ہے۔
ہم کیمرے/مائیکروفون کے ذریعے رسائی (اجازت) حاصل کرتے ہیں۔
اگلا، 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>
اب ویب ساکٹس کے ذریعے ٹرانسمیشن شامل کریں۔ حیرت کی بات یہ ہے کہ اس کے لیے آپ کو صرف ایک چیز کی ضرورت ہے۔
ہم ویڈیو اسٹریم کو سرور پر منتقل کرتے ہیں۔
<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>
کافی دیر تک میں نے یہ سمجھنے کی کوشش کی کہ موصول ہونے والے ٹکڑوں کو فوری طور پر پلے بیک کے لیے ویڈیو عنصر پر بھیجنا کیوں ناممکن ہے، لیکن معلوم ہوا کہ ایسا نہیں کیا جا سکتا، یقیناً، آپ کو پہلے اس ٹکڑے کو ایک خاص بفر میں رکھنا چاہیے۔ ویڈیو عنصر، اور اس کے بعد ہی یہ ویڈیو سٹریم چلانا شروع کر دے گا۔ اس کے لیے آپ کی ضرورت ہوگی۔
میڈیا سورس میڈیا پلے بیک آبجیکٹ اور اس میڈیا اسٹریم کے ماخذ کے درمیان ایک قسم کے بیچوان کے طور پر کام کرتا ہے۔ میڈیا سورس آبجیکٹ میں ویڈیو/آڈیو اسٹریم کے ماخذ کے لیے پلگ ایبل بفر ہوتا ہے۔ ایک خصوصیت یہ ہے کہ بفر صرف 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 پیچھے رہ جائے گا؛ ہم اسے سرور پر منتقل کرنے سے پہلے ویڈیو اسٹریم کو تقسیم کرتے وقت خود ترتیب دیتے ہیں۔ مزید برآں، جب میں نے اپنے لیپ ٹاپ پر چیک کیا تو، ترسیل اور وصول کرنے والے اطراف کے درمیان وقفہ آہستہ آہستہ جمع ہوتا گیا، یہ واضح طور پر دکھائی دے رہا تھا۔ میں نے اس نقصان پر قابو پانے کے طریقے تلاش کرنا شروع کیے، اور...
ماخذ: www.habr.com