Revision 68725001

View differences:

Public/assets/partyhub.js
1
var server = null;
2
if(window.location.protocol === 'http:')
3
	server = "http://" + window.location.hostname + ":8088/janus";
4
else
5
	server = "https://" + window.location.hostname + ":8089/janus";
6

  
7
var janus = null;
8
var janus_plugin = null;
9
var myid;
10
var myroom;
11
var description;
12
var ps_contact;
13
var r_channel;
14
var opaqueId = "streamingtest-"+Janus.randomString(12);
15

  
16
var stanza = (new URL(window.location.href)).searchParams.get("stanza");
17
if (!stanza)
18
	stanza = "unnamed"
19

  
20
$(document).ready(function() {
21
	// Initialize the library (all console debuggers enabled)
22
	myroom = Math.floor((Math.random() * 10000000) + 1);
23
	myid = Math.floor((Math.random() * 10000000) + 1);
24
	createJanus();
25
});
26

  
27
function startStream(selectedStream) {
28
	set_state("Room " + stanza);
29
	stopStream();
30
	Janus.log("Selected video id #" + selectedStream);
31
	if(selectedStream === undefined || selectedStream === null) {
32
		bootbox.alert("Select a stream from the list");
33
		return;
34
	}
35
	var body = { "request": "watch", id: parseInt(selectedStream) };
36
	streaming.send({"message": body});
37
	// No remote video yet
38
	if(spinner == null) {
39
		var target = document.getElementById('stream');
40
		spinner = new Spinner({top:100}).spin(target);
41
	} else {
42
		spinner.spin();
43
	}
44
}
45

  
46
function stopStream() {
47
	var body = { "request": "stop" };
48
	if (streaming) {
49
		streaming.send({"message": body});
50
		streaming.hangup();
51
	}
52
	//destroy janus?
53
}
54

  
55
function handleMessage(msg){
56
	var event = msg["videoroom"];
57
	Janus.debug("Event: " + event);
58
	if(event != undefined && event != null) {
59
		if(event === "joined") {
60
			// Publisher/manager created, negotiate WebRTC and attach to existing feeds, if any
61
			myid = msg["id"];
62
			mypvtid = msg["private_id"];
63
			Janus.log("Successfully joined room " + msg["room"] + " with ID " + myid);
64
			publishOwnFeed(true);
65
			// Any new feed to attach to?
66
			if(msg["publishers"] !== undefined && msg["publishers"] !== null) {
67
				var list = msg["publishers"];
68
				Janus.debug("Got a list of available publishers/feeds:");
69
				Janus.debug(list);
70
				for(var f in list) {
71
					var id = list[f]["id"];
72
					var display = list[f]["display"];
73
					var audio = list[f]["audio_codec"];
74
					var video = list[f]["video_codec"];
75
					Janus.debug("  >> [" + id + "] " + display + " (audio: " + audio + ", video: " + video + ")");
76
				}
77
			}
78
		} else if(event === "destroyed") {
79
			// The room has been destroyed
80
			Janus.warn("The room has been destroyed!");
81
			bootbox.alert("The room has been destroyed", function() {
82
			});
83
		}
84
	}
85
}
86

  
87
function publishOwnFeed(useAudio) {
88
	// Publish our stream
89
	janus_plugin.createOffer(
90
		{
91
			// Add data:true here if you want to publish datachannels as well
92
			media: { audioRecv: false, videoRecv: false, audioSend: useAudio, videoSend: true },	// Publishers are sendonly
93
			// If you want to test simulcasting (Chrome and Firefox only), then
94
			// pass a ?simulcast=true when opening this demo page: it will turn
95
			// the following 'simulcast' property to pass to janus.js to true
96
			simulcast: false,
97
			success: function(jsep) {
98
				Janus.debug("Got publisher SDP!");
99
				Janus.debug(jsep);
100
				var publish = { "request": "configure", "audio": useAudio, "video": true, "audiocodec": "opus", "videocodec": "vp8" };
101
				// You can force a specific codec to use when publishing by using the
102
				// audiocodec and videocodec properties, for instance:
103
				// 		publish["audiocodec"] = "opus"
104
				// to force Opus as the audio codec to use, or:
105
				// 		publish["videocodec"] = "vp9"
106
				// to force VP9 as the videocodec to use. In both case, though, forcing
107
				// a codec will only work if: (1) the codec is actually in the SDP (and
108
				// so the browser supports it), and (2) the codec is in the list of
109
				// allowed codecs in a room. With respect to the point (2) above,
110
				// refer to the text in janus.plugin.videoroom.cfg for more details
111
				janus_plugin.send({"message": publish, "jsep": jsep});
112
			},
113
			error: function(error) {
114
				Janus.error("WebRTC error:", error);
115
				if (useAudio) {
116
					 publishOwnFeed(false);
117
				} else {
118
					bootbox.alert("WebRTC error... " + JSON.stringify(error));
119
					$('#publish').removeAttr('disabled').click(function() { publishOwnFeed(true); });
120
				}
121
			}
122
		});
123
}
124
function createJanus() {
125
	Janus.init({debug: "all", callback: function() {
126
		// Create session
127
		janus = new Janus(
128
			{
129
				server: server,
130
				success: function() {
131
					// Attach to streaming plugin
132
					janus.attach(
133
						{
134
							plugin: "janus.plugin.videoroom",
135
							opaqueId: opaqueId,
136
							success: function(pluginHandle) {
137
								janus_plugin = pluginHandle;
138
								Janus.log("Plugin attached! (" + janus_plugin.getPlugin() + ", id=" + janus_plugin.getId() + ")");
139
								requestRoomCreation();
140
							},
141
							error: function(error) {
142
								Janus.error("  -- Error attaching plugin... ", error);
143
								bootbox.alert("Error attaching plugin... " + error);
144
							},
145
							onmessage: function(msg, jsep) {
146
								Janus.debug(" ::: Got a message :::");
147
								Janus.debug(msg);
148
								handleMessage(msg);
149
								if(jsep !== undefined && jsep !== null) {
150
									Janus.debug("Handling SDP as well...");
151
									Janus.debug(jsep);
152
									janus_plugin.handleRemoteJsep({jsep: jsep});
153
								}
154
								},
155
								onlocalstream: function(stream) {
156
									Janus.debug(" ::: Got a local stream :::");
157
									mystream = stream;
158
									Janus.debug(stream);
159
									Janus.attachMediaStream($('#myvideo').get(0), stream);
160
									$("#myvideo").get(0).muted = "muted";
161
									var videoTracks = stream.getVideoTracks();
162
									if(videoTracks === null || videoTracks === undefined || videoTracks.length === 0) {
163
										// No webcam
164
										$('#plugin').append(
165
											'<div class="no-video-container">' +
166
												'<i class="fa fa-video-camera fa-5 no-video-icon" style="height: 100%;"></i>' +
167
												'<span class="no-video-text" style="font-size: 16px;">No webcam available</span>' +
168
											'</div>');
169
									}
170
								},
171
								onremotestream: function(stream) {
172
									Janus.debug(" ::: Got a remote stream :::");
173
									Janus.debug(stream);
174
								},
175
								oncleanup: function() {
176
									Janus.log(" ::: Got a cleanup notification :::");
177
									mystream = null;
178
									$('#myvideo').remove();
179
								}
180
							});
181
					// streaming plugin
182
					janus.attach(
183
						{
184
							plugin: "janus.plugin.streaming",
185
							opaqueId: opaqueId,
186
							success: function(pluginHandle) {
187
								streaming = pluginHandle;
188
								Janus.log("Plugin attached! (" + streaming.getPlugin() + ", id=" + streaming.getId() + ")");
189
							},
190
							error: function(error) {
191
								Janus.error("  -- Error attaching plugin... ", error);
192
								bootbox.alert("Error attaching plugin... " + error);
193
							},
194
							onmessage: function(msg, jsep) {
195
								Janus.debug(" ::: Got a message :::");
196
								Janus.debug(msg);
197
								var result = msg["result"];
198
								if(result !== null && result !== undefined) {
199
									if(result["status"] !== undefined && result["status"] !== null) {
200
										var status = result["status"];
201
										if(status === 'starting')
202
											$('#status').text("Starting, please wait...").show();
203
										else if(status === 'started')
204
											$('#status').text("Started").show();
205
										else if(status === 'stopped')
206
											stopStream();
207
									} 
208
								} else if(msg["error"] !== undefined && msg["error"] !== null) {
209
									bootbox.alert(msg["error"]);
210
									stopStream();
211
									return;
212
								}
213
								if(jsep !== undefined && jsep !== null) {
214
									Janus.debug("Handling SDP as well...");
215
									Janus.debug(jsep);
216
										// Answer
217
									streaming.createAnswer(
218
										{
219
											jsep: jsep,
220
											media: { audioSend: false, videoSend: false },	// We want recvonly audio/video
221
											success: function(jsep) {
222
												Janus.debug("Got SDP!");
223
												Janus.debug(jsep);
224
												var body = { "request": "start" };
225
												streaming.send({"message": body, "jsep": jsep});
226
											},
227
											error: function(error) {
228
												Janus.error("WebRTC error:", error);
229
												bootbox.alert("WebRTC error... " + JSON.stringify(error));
230
											}
231
										});
232
								}
233
								},
234
								onremotestream: function(stream) {
235
									Janus.debug(" ::: Got a remote stream :::");
236
									Janus.debug(stream);
237
									if($('#remotevideo').length > 0) {
238
										// Been here already: let's see if anything changed
239
										var videoTracks = stream.getVideoTracks();
240
										if(videoTracks && videoTracks.length > 0 && !videoTracks[0].muted) {
241
											if($("#remotevideo").get(0).videoWidth)
242
												$('#remotevideo').show();
243
										}
244
										return;
245
									}
246
									remote_video = r_channel.split("-")[1];
247
									$('#stream').append('<video class="rounded hide" id="' + remote_video + '" width=320 height=240 autoplay/><span>Loading '+remote_video+'</span>');
248
									// Show the stream and hide the spinner when we get a playing event
249
									$("#"+remote_video).bind("playing", function () {
250
										if(this.videoWidth)
251
											$('#'+remote_video).removeClass('hide').show();
252
										if(spinner !== null && spinner !== undefined)
253
											spinner.stop();
254
										spinner = null;
255
										var videoTracks = stream.getVideoTracks();
256
										if(videoTracks === null || videoTracks === undefined || videoTracks.length === 0)
257
											return;
258
										var width = this.videoWidth;
259
										var height = this.videoHeight;
260
										if(Janus.webRTCAdapter.browserDetails.browser === "firefox") {
261
											// Firefox Stable has a bug: width and height are not immediately available after a playing
262
											setTimeout(function() {
263
												var width = $('#'+remote_video).get(0).videoWidth;
264
												var height = $('#'+remote_video).get(0).videoHeight;
265
											}, 2000);
266
										}
267
									});
268
									var videoTracks = stream.getVideoTracks();
269
									if(videoTracks && videoTracks.length &&
270
											(Janus.webRTCAdapter.browserDetails.browser === "chrome" ||
271
												Janus.webRTCAdapter.browserDetails.browser === "firefox" ||
272
												Janus.webRTCAdapter.browserDetails.browser === "safari")) {
273
										$('#curbitrate').removeClass('hide').show();
274
									}
275
									Janus.attachMediaStream($('#'+remote_video).get(0), stream);
276
									var videoTracks = stream.getVideoTracks();
277
									if(videoTracks === null || videoTracks === undefined || videoTracks.length === 0 || videoTracks[0].muted) {
278
										// No remote video
279
										$('#'+remote_video).hide();
280
									}
281
								},
282
								oncleanup: function() {
283
									Janus.log(" ::: Got a cleanup notification :::");
284
									$('#remotevideo').remove();
285
									$('.no-video-container').remove();
286
								}
287
							});
288

  
289
					},
290
					error: function(error) {
291
						Janus.error(error);
292
						bootbox.alert(error, function() {
293
							window.location.reload();
294
						});
295
					},
296
					destroyed: function() {
297
					}
298
				});
299
	}});
300
}
301

  
302
function startStreaming() {
303
	var register = { "request": "joinandconfigure", "bitrate": 128000, "room": myroom, "ptype": "publisher", "display": description, "id": myid };
304
	janus_plugin.send({"message": register});
305

  
306
	var xhttp = new XMLHttpRequest();
307
	xhttp.onreadystatechange = function() {
308
		if (this.readyState == 4 && this.status == 200) {
309
			$('#creation_form').hide();
310
		}
311
		if (this.readyState == 4 && this.status != 200) {
312
			set_state("&lt;An error occurred with the streaming process&gt;");
313
		}
314
	};
315

  
316
	xhttp.open("UPDATE", "/sources/" + myroom, true);
317
	xhttp.setRequestHeader("Content-type", "application/x-www-form-urlencoded");
318
	var params = "participant_id=" + encodeURIComponent(myid) + "&channel_name=" + encodeURIComponent(ps_contact);
319
	xhttp.send(params);
320
}
321

  
322
function requestRoomCreation() {
323
	var xhttp = new XMLHttpRequest();
324
	xhttp.onreadystatechange = function() {
325
		if (this.readyState == 4 && this.status == 200) {
326
			set_state("Room " + stanza);
327
			var btn = document.getElementById("submit");
328
			btn.onclick = function(){	
329
				description = document.getElementById("description").value;
330
				ps_contact = "conf_stanza:" + stanza + "-" + description;
331
				startStreaming();
332
			};
333
		}
334
		if (this.readyState == 4 && this.status != 200) {
335
			set_state("&lt;An error occurred with room creation&gt; " + this.status);
336
		}
337
	};
338
	xhttp.open("POST", "/sources/" + myroom, true);
339
	xhttp.setRequestHeader("Content-type", "application/x-www-form-urlencoded");
340
	xhttp.send();
341
}
342

  
343
function refresh_contacts()
344
{
345
	var xhttp = new XMLHttpRequest();
346
	xhttp.onreadystatechange = function() {
347
		if (this.readyState == 4 && this.status == 200) {
348
			var channels = JSON.parse(this.responseText);
349
			update_contacts(channels);
350
		}
351
	};
352
	xhttp.open("GET", "/channels", true);
353
	xhttp.send();
354
}
355

  
356
function update_contacts(chs)
357
{
358
	var list = document.getElementById("channel-list");
359
	while (list.firstChild) {
360
		list.removeChild(list.firstChild);
361
	}
362
	for (el in chs) {
363
		ch_stanza = chs[el].name.split("-")[0].split(":")[1]
364
		name = chs[el].name.split("-")[1]
365

  
366
		if (ch_stanza == stanza && name != description)
367
		{
368
			var node = document.createElement("LI");
369
			node.className="list-group-item btn btn-default";
370
			var textnode = document.createTextNode(name);
371
			node.appendChild(textnode);
372
			list.appendChild(node);
373
			(function (ch) {
374
			node.onclick = function(e){request_channel(ch);};
375
			})(chs[el]);
376
		}
377
	}
378
}
379

  
380

  
381
setInterval(update_source, 3000);
382
refresh_contacts();
383
var refresh_channels_id = setInterval(refresh_contacts, 5000);
384
var update_channel_id = setInterval(update_channel, 3000);
385
var channel = "";
Public/assets/psng.js
1 1
function response_channel(ch)
2 2
{
3
	r_channel = ch.name;
3 4
	channel = ch.id;
4 5
	set_state(ch.name);
5 6
	startStream(ch.janus_streaming_id);
......
37 38
		list.removeChild(list.firstChild);
38 39
	}
39 40
	for (el in chs) {
40
		var node = document.createElement("LI");
41
		node.className="list-group-item btn btn-default";
42
		var textnode = document.createTextNode(chs[el].name);
43
		node.appendChild(textnode);
44
		list.appendChild(node);
45
		(function (ch) {
46
		node.onclick = function(e){request_channel(ch);};
47
		})(chs[el]);
41
		conf = chs[el].name.split(":")[0]
42
		if (conf != "conf_stanza")
43
		{
44
			var node = document.createElement("LI");
45
			node.className="list-group-item btn btn-default";
46
			var textnode = document.createTextNode(chs[el].name);
47
			node.appendChild(textnode);
48
			list.appendChild(node);
49
			(function (ch) {
50
			node.onclick = function(e){request_channel(ch);};
51
			})(chs[el]);
52
		}
48 53
	}
49 54
}
50 55

  
Public/mysource.html
42 42
				<ul class="nav navbar-nav">
43 43
					<li><a href="/player.html">Player</a></li>
44 44
					<li class="active"><a href="/mysource.html">My Source</a></li>
45
					<li><a href="/partyhub.html">PartyHub</a></li>
45 46
					<li><a href="#">About</a></li>
46 47
				</ul>
47 48
			</div><!--/.nav-collapse -->
Public/partyhub.html
1
<!DOCTYPE html>
2
<html>
3
<head>
4
<meta charset="utf-8">
5
<meta name="viewport" content="width=device-width, initial-scale=1.0"/>
6
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
7
	<title>PeerSteamer-ng</title>
8

  
9
	<!-- Twitter Bootstrap -->
10
<script type="text/javascript" src="https://cdnjs.cloudflare.com/ajax/libs/jquery/1.9.1/jquery.min.js" ></script>
11
	<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/css/bootstrap.min.css" integrity="sha384-BVYiiSIFeK1dGmJRAkycuHAHRg32OmUcww7on3RYdg4Va+PmSTsz/K68vbdEjh4u" crossorigin="anonymous">
12
	<script src="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/js/bootstrap.min.js" integrity="sha384-Tc5IQib027qvyjSMfHjOMaLkfuWVxZxUPnCJA7l2mCWNIpG9mGCD8wGNIcPD7Txa" crossorigin="anonymous"></script>
13
<script type="text/javascript" src="https://cdnjs.cloudflare.com/ajax/libs/webrtc-adapter/5.0.1/adapter.min.js" ></script>
14
<script type="text/javascript" src="https://cdnjs.cloudflare.com/ajax/libs/bootbox.js/4.1.0/bootbox.min.js"></script>
15
<script type="text/javascript" src="https://cdnjs.cloudflare.com/ajax/libs/spin.js/2.3.2/spin.min.js"></script>
16
<script type="text/javascript" src="https://cdnjs.cloudflare.com/ajax/libs/toastr.js/2.1.3/toastr.min.js"></script>
17
	<script src="/assets/janus.js"></script>
18
	<script src="/assets/psng.js"></script>
19
<script src="/assets/partyhub.js"></script> 
20

  
21
</head>
22
<body>
23
	<div id=main-container class=container>
24
		<div class=jumbotron>
25
			<h1>PeerStreamer-ng</h1>
26
			Your friendly-neighborhood streaming platform
27
		</div>
28
	</div>
29

  
30
	
31
	<nav class="navbar navbar-default ">
32
		<div class="container-fluid">
33
			<div class="navbar-header">
34
				<button type="button" class="navbar-toggle collapsed" data-toggle="collapse" data-target="#navbar" aria-expanded="false" aria-controls="navbar">
35
					<span class="sr-only">Toggle navigation</span>
36
					<span class="icon-bar"></span>
37
					<span class="icon-bar"></span>
38
					<span class="icon-bar"></span>
39
				</button>
40
			</div>
41
			<div id="navbar" class="navbar-collapse collapse">
42
				<ul class="nav navbar-nav">
43
					<li><a href="/player.html">Player</a></li>
44
					<li><a href="/mysource.html">My Source</a></li>
45
					<li class="active"><a href="/partyhub.html">PartyHub</a></li>
46
					<li><a href="#">About</a></li>
47
				</ul>
48
			</div><!--/.nav-collapse -->
49
		</div><!--/.container-fluid -->
50
	</nav>
51

  
52

  
53
	<div class="row center-block">
54
		<div class="col-md-10">
55
			<div class="row center-block">
56
				<h2 id="player-title" class="text-center">&lt;PartyHub&gt;</h2>
57
			</div>
58
			<div class="row text-center" id="creation_form">
59
				<input type=text id="description" value="John"/>
60
				<input type=button id="submit" value="Add yourself"/>
61
			</div>
62
			<div id="plugin" class="row text-center" >
63
							<div class="panel-body" id="stream"></div>
64
							<video class="rounded " id="myvideo" width="400" height="300" autoplay />
65
							<span id="status"></span>
66
			</div>
67
		</div>
68
		<div class="col-md-2">
69
			<div class="row center-block">
70
				<h2 class="text-center">Contacts</h2>
71
			</div>
72
			<ul class="list-group" id="channel-list">
73
			</ul>
74
		</div>
75
	</div>
76
</body>
77
</html>
Public/player.html
42 42
				<ul class="nav navbar-nav">
43 43
					<li class="active"><a href="/player.html">Player</a></li>
44 44
					<li><a href="/mysource.html">My Source</a></li>
45
					<li><a href="/partyhub.html">PartyHub</a></li>
45 46
					<li><a href="#">About</a></li>
46 47
				</ul>
47 48
			</div><!--/.nav-collapse -->

Also available in: Unified diff