Browser-Aware Player Code: Episode V, IE Strikes Back

Not so long ago, I updated my browser-aware player code to check for the presence of a stream. Recently, it’s come to light that Internet Explorer 9 doesn’t play nice with this particular snippet, because in IE9, the Javascript engine is rather brain-damaged when it comes to cross-site requests. In order to deal with this properly, we must alter the way we query the server for the presence of a stream:


console.log("Starting stream Check");
if (is_ie9) {
console.log ("Using XDR because IE9 is stupid");
streamcheckXDR();
setInterval(function(){streamcheckXDR()},10000);
}
else {
streamcheck();
setInterval(function(){streamcheck()},10000);
}
function streamcheckXDR() {
 console.log("Starting XDR");
 xdr = new XDomainRequest();
 if(xdr) {
 xdr.onerror = function(){ console.log("XDR Error"); };
 xdr.onload = function(){ startPlayer(xdr.responseText,"XDR"); };
 url = "http://"+streamer+":8086/streamcheck?stream="+stream;
 xdr.open("get",url);
 xdr.send();
 } else {
 console.log("Failed to create XDR object");
 }

}

function startPlayer(result,mode){

// for some inexplicable reason, running "Boolean" on the XDR output doesn't work

// so we have to call the function and tell it if we're dealing with XDR data or AJAX data.

 

if(mode == "XDR") {
 if (result === "true") { curstatus = true;}
 if (result === "false") { curstatus = false;}
 } else {
 curstatus = Boolean(result);
 }

//console.log("Result: "+result);
 //console.log("Previous: "+prevstatus);
 //console.log("Current: "+curstatus);
 if (curstatus == prevstatus) {
 //console.log("No Change");
 } else {
 if (curstatus) {
 if (is_iphone || is_ipad || is_ipod) { iOSPlayer("videoframe",plwd,plht,server,stream);}
 else if (is_blackberry) { rtspPlayer("videoframe",plwd,plht,server,stream+'_+240p');}
 else { flashPlayer("videoframe",plwd,plht,server,stream); }
 console.log("Changed from false to true");
 } else {
 var vframe=document.getElementById("videoframe")
 if (is_iphone || is_ipad || is_ipod || is_blackberry) {
 } else {
 jwplayer("videoframe").remove();
 }
 vframe.innerHTML = '<IMG SRC="image.png" WIDTH="'+plwd+'" HEIGHT="'+plht+'">';


 console.log("Changed from true to false");
 }

}
 prevstatus = curstatus;
}

function streamcheck() {
 console.log("Starting AJAX");
 $.ajax({
 dataType: "json",
 contentType: "text/plain",

type: "GET",
 url: "http://"+streamer+":8086/streamcheck?stream="+stream,
 error: function(XMLHttpRequest, textStatus, errorThrown)
 {
 console.log('AJAX Failure:'+ textStatus+':'+errorThrown);
 //some stuff on failure
 },
 success: function(result){startPlayer(result)}
 });
}

Switching Live Streams

Continuing on the rack theme mentioned yesterday, I got to wondering about a stream monitor that could be switched to any of a number of live streams, without reloading the page. Fortunately, JW Player makes this easy. I threw together a player embedded inside some CSS and then added a button panel. Each button is a DIV with an onclick() action that calls jwplayer().load(). While it’s well known that you can use this to switch files simply by passing the filename to the file flashvar, we need to also pass the streamer value. Fortunately, the load() method has the ability to pass on not only files as a string value, but also objects, which are nothing more than an array of flashvars (it can also take playlist items, but that’s beyond the scope of this post). So, all you need to do in order to switch streams with JW Player is call the following JavaScript method:

jwplayer().load({file: 'streamname', streamer: 'rtmp://w.streampunk.tv/live'})

The result I got was this:Clicking on each of the buttons in the bar below the screen will switch the stream. A live example can be found here. If you want to see what else the JWPlayer API can do, head on over to the API docs. FlowPlayer appears to have something similar in the clip.update() method (flowplayer API documentation), but I haven’t tested it.

Page code:


<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>

	<title>JW Player Switching</title>

	<style type="text/css">
		body { background-color: #fff; padding: 0 20px; color:#000; font: 13px/18px Arial, sans-serif; }
		a { color: #360; }
		h3 { padding-top: 20px; }
		ol { margin:5px 0 15px 16px; padding:0; list-style-type:square; }
	</style>

</head>
<body>
	<!-- START OF THE PLAYER EMBEDDING TO COPY-PASTE -->
	<div class="rack" style="width: 600px;">
	<div class="rackmonitor" style="width: 600px; height: 323px; background-image: url('rackmonitor.png'); position: relative">
	&nbsp;
	<div class="screen" style="width: 455px; height: 275px; position: absolute; left: 60px; top: 28px;">
	<div id="mediaplayer">Player...</div>
	<script type="text/javascript" src="/jwplayer/jwplayer.js"></script>
	<script type="text/javascript">
		jwplayer("mediaplayer").setup({
			flashplayer: "/jwplayer/player.swf",
			height: "275",
			width: "455",
			file: "commons.stream",
			streamer: "rtmp://w.streampunk.tv/live",
			autostart: true,
			controlbar: 'none',
			mute: true
		});
	</script>
	</div>
	</div>
	</div>
	<div class="panel" 
	style="width: 600px; 
	height: 57px; 
	background-image: url('blankPanel.png'); 
	position: relative
	">	
		<div class="button1" 
		style="width: 75px; 
		height: 20px; 
		position: absolute; 
		left: 50px; 
		top: 20px; 
		border-radius: 10px; 
		background-color: #009900; 
		color: #cccccc; 
		border: 1px solid black; 
		text-align: center;" 
		onclick="jwplayer().load({file: 'playlist-high', streamer: 'rtmp://wms.rezonline.org/redirect'})
		">
		RezOnline
		</div>
		<div class="button2" 
		style="width: 75px; 
		height: 20px; 
		position: absolute; 
		left: 150px; 
		top: 20px; border-radius: 10px; 
		background-color: #009900;
		color: #cccccc; 
		border: 1px solid black;text-align: center;" 
		onclick="jwplayer().load({file: 'commons.stream', streamer: 'rtmp://w.streampunk.tv/live'})
		">
		Commons
		</div>
		<div class="button3" 
		style="width: 75px; 
		height: 20px; 
		position: absolute; 
		left: 250px; 
		top: 20px; border-radius: 10px; 
		background-color: #990000;
		color: #cccccc; 
		border: 1px solid black;text-align: center;" 
		onclick="jwplayer().load({file: 'streamclass', streamer: 'rtmp://w.streampunk.tv/live'})
		">
		StreamClass
		</div>
		<div class="button4" 
		style="width: 75px; 
		height: 20px; 
		position: absolute; 
		left: 350px; 
		top: 20px; border-radius: 10px; 
		background-color: #990000;
		color: #cccccc; 
		border: 1px solid black;text-align: center;" 
		onclick="jwplayer().load({file: 'aircam.stream', streamer: 'rtmp://w.streampunk.tv/live'})
		">
		AirCam
		</div>
	</div>
</body>
</html>

Browser-Aware Player

One of the big challenges of streaming to the web is the sheer diversity of devices out there.

This past week, I pushed out some modifications to the player code on our live page that switches the player code based on what the user is connecting with. The genesis of this change was a problem with our change to JW Player Version 5 causing our PlayStation users to no longer be able to watch our video since JW v5 requires Flash 10 and Sony apparently doesn’t care about its customers. After a successful test with the Playstation, I extended the code to provide an HTML5 <VIDEO> tag for our iPhone users (allowing us to clear up some the clutter on the sidebar), as well as MMS and RTSP links around a graphic mimicking the Flash-based player in order to provide a consistent user experience for our Android/WebOS/BlackBerry/WinMo users.

EDIT: The main reason I’m not doing straight HTML5 with Flash fallback (a much more elegant solution) is that we’re sending out VP6 for our flash users and a lower-bandwidth h.264 stream for our mobile users. We’re not currently using h.264 for our flash users because of the poor quality of the h.264 encoder in Flash Media Live Encoder. Once we get a “real encoder“, we’ll send out a single set of h.264 streams and use HTML5 with fallback.

The code is here.

Y2K10 in JavaScript?

On our live stream page, we have a nifty little javascript counter that lets you know when the next service is.

Leo noticed today that in Internet Explorer, it’s counting down properly, while in Firefox, it’s saying the event is already happening. On a hunch, we changed the target date to 12/31, and it started working properly again.

So, IE’s Javascript is smart enough to figure out that on December 29, the target date of January 3 is likely to be the one next week. Firefox is clinging to the past and assuming that I really meant the January 3 that happened 51 weeks ago.

How is it that the same script can be interpreted so differently within the same language on two different browser platforms? This stuff is supposed to be standard!