Creating a video broadcast on JS

Good day, dear habrachitatel.
Today I will tell you how you can organize a live broadcast of an image from a webcam using HTML5 / JS and NodeJS, as well as PHP.

The article observes a fairly large amount of code. Also, this article focuses more on beginner web developers than on professionals.

Client part

First you need to create an element of type “video”, into which the stream from navigator.getUserMedia will be copied , as well as canvas, from which the image will be taken for sending:




Next, you need to direct the stream from getUserMedia to video:

var video = document.querySelector("#vid"),
       canvas = document.querySelector('#canvas'),
       ctx = canvas.getContext('2d'),
       localMediaStream = null,
       onCameraFail = function (e) {
            console.log('Camera did not work.', e); // Исключение на случай, если камера не работает
        };
       navigator.getUserMedia = navigator.getUserMedia || navigator.webkitGetUserMedia || navigator.mozGetUserMedia || navigator.msGetUserMedia;
        window.URL = window.URL || window.webkitURL;
        navigator.getUserMedia({video: true}, function(stream) {
            video.src = window.URL.createObjectURL(stream);
            localMediaStream = stream;
        }, onCameraFail);


Well, at this stage, the video stream is cloned into the "video" tag, which by the way is not visible. Now we need to make the image constantly copied to canvas. To do this, set the timer that calls the copy procedure, and, accordingly, the copy procedure itself.

cameraInterval = setInterval(function(){ snapshot();}, 1);
function snapshot(){
       if(localMediaStream){
              ctx.drawImage(video, 0, 0);
        }
}


Well, now you can look at yourself, but the data has not yet been sent anywhere. To send data, you need to make sure that everything is synchronized and that the user agrees to broadcast, so add a couple of buttons to enable and disable the stream.



And we will write the procedure for sending data to the server.
But what data? To solve this problem, you can use base64 compression of everything on the canvas, and, accordingly, send this line to the server.

var isBroadcasting = false,
	broadcastingTimer;
function sendSnapshot(){
	if(localMediaStream && !isBroadcasting){
		isBroadcasting = true;
                $.post("/",
			{
				p: "new",
				text: ctx.canvas.toDataURL("image/webp", quality); // quality - качество изображения(float)
			},
			function(result){
				console.log(result); // На случай, если что-то пойдёт не так
				isBroadcasting = false;
			}
		);
	}
}
// И добавим обработчики кнопок начала и завершения вещания
function startBroadcasting(){
	broadcastingTimer = setInterval(sendSnapshot, 1);
}
function stopBroadcasting(){
	clearInterval(broadcastingTimer);
}


The client part of the broadcast is over.

Server side (Node JS)

Such an application does not require any sophistication or server frameworks. The server is created using the http.createServer function.
The main thing here is to add a handler in case a post request arrives.

var qs = require('querystring');
var imageData = "";
var myId = 1;
/* Запуск сервера */
if(req.method == "POST"){
	var fullBody = "";
	req.on('data', function(chunk){
		fullBody += chunk.toString();
	});
	req.on('end', function(){
		res.writeHead(200, {'Content-Type': 'text/html'});
		var POST = qs.parse(fullBody);
		if(POST["p"] == "new"){ // Смена изображения
			imageData = POST["text"];
			myId += 1;
			res.write(imageData);
		}else if(POST["p"] == "ajax"){
			if(myId > parseInt(POST["last"])){
				if(typeof(imageData) != "undefined"){
					res.write(document.body.innerHTML = ('');" + "\n");
					res.write("last_message_id = " + myId + ";");
				}
			}
		}
		res.end();
	});
}else{ /* Здесь идёт отдача всего, что пользователь просит. */}


Similarly with php:

Php code
if($_GET['p'] == "ajax"){
	Header("Cache-Control: no-cache, must-revalidate");
	Header("Pragma: no-cache");
	Header("Content-Type: text/javascript; charset=windows-1251");
	$file = file("monitor_id.txt");
	$id = $file[0];
	if($id > $_GET['last']){
		$text_file = file("monitor_command.txt");
		$count = count($text_file);
		$last = $id;
		echo "var main = $('#main'); \n";
		for($i = 0; $i < 1; $i++){
			$s = $text_file[$i];
			while(strpos($s, chr(92)) !== false){
				$s = str_replace(chr(92), "", $s);
			}
			echo $s;
		}
		echo "\n";
    	echo "last_message_id = $id;";
	}
}elseif((isset($_GET['p']) && $_GET['p'] == "new") || (isset($_POST['p']))){
	$file = file("monitor_id.txt");
	$id = $file[0];
	$fh = fopen("monitor_command.txt", "w+");
	$get_text = $_POST['text'];
	$gt = $get_text;
	while(strpos($get_text, "\r\n") !== false){
		$get_text = str_replace("\r\n", "
", $get_text); } fwrite($fh, "document.body.innerHTML = (');\n"); fclose($fh); $fhn = fopen("monitor_id.txt", "w+"); fwrite($fhn, $id + 1); fclose($fhn); echo $get_text; }



Client part (broadcast viewing)

Oddly enough, the client part is very simple, it only requires the execution of the code coming from the server.
var last_message_id = 0, 
	load_in_process = false; 
function Load() {
    if(!load_in_process)
    {
	    load_in_process = true;
    	$.post("/", 
    	{
      	    p: "ajax", 
      	    last: last_message_id,
			version: version
    	},
   	    function (result) {
		    eval(result);
		    load_in_process = false; 
    	});
    }
}
var loadInterval = setInterval(Load, 1);


Conclusion

Today we wrote a simple service for organizing one-way video broadcasts using html5 / js. It should only be noted that so far this solution does not work very quickly due to the lack of normal compression, as well as due to the fact that all image processing operations can be performed only on the client, and from this the frame rate decreases and begins to be observed " hangs. "

But this rather simple example is just a proof that html5 is not so far from flash anymore, and that the implementation of many things becomes possible.

Thanks for attention!

Also popular now: