Archive for October, 2010

Non-Affine transforms, Revised for iOS

It seems WebKitCSSMatrix’s multiply() method is broken on mobile safari (including my 4.1 iPhone). ¬†Implementing multiply() manually seems to work.

Here’s a revised version of the previous example, which will show different output on desktop safari as from mobile safari.

Apologies for the debugging mess though.

<!DOCTYPE HTML>
<html>
	<head>
		<title>css 3d test</title>
		<style type="text/css">
			body {
				margin: 0px;
			}
			#thing {
				-webkit-transform-origin: top left;
				opacity: .5;
				/*	I can get rid of the manual scaling in the javascript with this additon:
				 width: 1px;
				 height: 1px; */
				position: absolute;
			}
			.point {
				width: 8px;
				height: 8px;
				background-color: pink;
				position: absolute;
				margin-left: -4px;
				margin-top: -4px;
			}
			</style>
		</head>
	<body>
		<img src="IMG_0026_512x512.jpg" id="thing">

			<div id="p0" class="point" style="left: 130px; top: 100px; background-color: red;"> </div>
			<div id="p2" class="point" style="left: 200px; top: 100px; background-color: green;"> </div>
			<div id="p3" class="point" style="left: 210px; top: 210px; background-color: blue;"> </div>
			<div id="p1" class="point" style="left: 100px; top: 200px; background-color: yellow;"> </div>

			<script type="text/javascript">
				/*
				 non-affine transforms:
				 http://www.charlespetzold.com/blog/2007/08/250638.html

				 safari 3d transform introduction (css)
				 http://webkit.org/blog/386/3d-transforms/

				 some apple documentation:
				 http://developer.apple.com/library/safari/#documentation/InternetWeb/Conceptual/SafariVisualEffectsProgGuide/Transforms/Transforms.html

				 reference: http://www.w3.org/TR/css3-3d-transforms/
				 */

				//(function () {

				// create a non-affine transform to move #thing's corners to those
				// specified by p0 through p3
				var p0 = document.getElementById('p0');
				var p1 = document.getElementById('p1');
				var p2 = document.getElementById('p2');
				var p3 = document.getElementById('p3');
				var thing = document.getElementById('thing');

				var x0 = parseFloat(p0.style.left, 10)
				var y0 = parseFloat(p0.style.top, 10)
				var x1 = parseFloat(p1.style.left, 10)
				var y1 = parseFloat(p1.style.top, 10)
				var x2 = parseFloat(p2.style.left, 10)
				var y2 = parseFloat(p2.style.top, 10)
				var x3 = parseFloat(p3.style.left, 10)
				var y3 = parseFloat(p3.style.top, 10)

				var arrayToMatrix = function (array) {
					// this is a cheesy hack, I think a better approach would be
					//var x = new WebKitCSSMatrix(); x.m11=array[0] and so-on
					//return new WebKitCSSMatrix('matrix3d(' + array.join(', ') + ')');
					var result = new WebKitCSSMatrix();
					result.m11 = array[0];
					result.m12 = array[1];
					result.m13 = array[2];
					result.m14 = array[3];
					result.m21 = array[4];
					result.m22 = array[5];
					result.m23 = array[6];
					result.m24 = array[7];
					result.m31 = array[8];
					result.m32 = array[9];
					result.m33 = array[10];
					result.m34 = array[11];
					result.m41 = array[12];
					result.m42 = array[13];
					result.m43 = array[14];
					result.m44 = array[15];
					return result;
				}

				var mulMat = function(a, b) {
					var result = new WebKitCSSMatrix();
					// matrix multiplication:
					// result.m12 = row 1 of a dot column 2 of b
					result.m11 =  a.m11*b.m11 + a.m21*b.m12 + a.m31*b.m13 + a.m41*b.m14
					result.m12 =  a.m12*b.m11 + a.m22*b.m12 + a.m32*b.m13 + a.m42*b.m14
					result.m13 =  a.m13*b.m11 + a.m23*b.m12 + a.m33*b.m13 + a.m43*b.m14
					result.m14 =  a.m14*b.m11 + a.m24*b.m12 + a.m34*b.m13 + a.m44*b.m14
					result.m21 =  a.m11*b.m21 + a.m21*b.m22 + a.m31*b.m23 + a.m41*b.m24
					result.m22 =  a.m12*b.m21 + a.m22*b.m22 + a.m32*b.m23 + a.m42*b.m24
					result.m23 =  a.m13*b.m21 + a.m23*b.m22 + a.m33*b.m23 + a.m43*b.m24
					result.m24 =  a.m14*b.m21 + a.m24*b.m22 + a.m34*b.m23 + a.m44*b.m24
					result.m31 =  a.m11*b.m31 + a.m21*b.m32 + a.m31*b.m33 + a.m41*b.m34
					result.m32 =  a.m12*b.m31 + a.m22*b.m32 + a.m32*b.m33 + a.m42*b.m34
					result.m33 =  a.m13*b.m31 + a.m23*b.m32 + a.m33*b.m33 + a.m43*b.m34
					result.m34 =  a.m14*b.m31 + a.m24*b.m32 + a.m34*b.m33 + a.m44*b.m34
					result.m41 =  a.m11*b.m41 + a.m21*b.m42 + a.m31*b.m43 + a.m41*b.m44
					result.m42 =  a.m12*b.m41 + a.m22*b.m42 + a.m32*b.m43 + a.m42*b.m44
					result.m43 =  a.m13*b.m41 + a.m23*b.m42 + a.m33*b.m43 + a.m43*b.m44
					result.m44 =  a.m14*b.m41 + a.m24*b.m42 + a.m34*b.m43 + a.m44*b.m44
					return result;
				}

				// 2048x1536
				var matScaleDown = new WebKitCSSMatrix().scale(1/512,1/512,1);

				var matAffineTransform = arrayToMatrix([x2-x0,y2-y0,0,0,
														x1-x0,y1-y0,0,0,
														0,0,1,0,
														0,0,0,1]);

				var matTranslate = new WebKitCSSMatrix().translate(x0,y0,0);

				var a = (matAffineTransform.m22 * x3 -
						 matAffineTransform.m21 * y3 +
						 matAffineTransform.m21 * y0 -
						 matAffineTransform.m22 * x0) /
				(matAffineTransform.m11 * matAffineTransform.m22 -
				 matAffineTransform.m12 * matAffineTransform.m21)
				var b = (matAffineTransform.m11 * y3 -
						 matAffineTransform.m12 * x3 +
						 matAffineTransform.m12 * x0 -
						 matAffineTransform.m11 * y0) /
				(matAffineTransform.m11 * matAffineTransform.m22 -
				 matAffineTransform.m12 * matAffineTransform.m21)
				var matNonAffineTransform = arrayToMatrix([a/(a+b-1),0,0,a/(a+b-1)-1,
														   0,b/(a+b-1),0,b/(a+b-1)-1,
														   0,0,1,0,
														   0,0,0,1]);

				// note: the matrix specified to multiply() is multiplied on the right
				// so the matrices listed below are in reverse order:
				var matResult = new WebKitCSSMatrix().
				multiply(matTranslate).
				multiply(matAffineTransform).
				multiply(matNonAffineTransform).
				multiply(matScaleDown);

				// use my own mulMat() instead of the possibly broken WebKitCSSMatrix.multiply()
				var matResult2 = new WebKitCSSMatrix();
				matResult2 = mulMat(matResult2, matTranslate);
				matResult2 = mulMat(matResult2, matAffineTransform);
				matResult2 = mulMat(matResult2, matNonAffineTransform);
				matResult2 = mulMat(matResult2, matScaleDown);

				document.write(matResult);
				document.write('<br />');
				document.write(matResult2);

				thing.style['-webkit-transform'] = matResult2;

				//})();
				</script>
		</body>
	</html>
Advertisements

Leave a comment

Your own live stream

I think the below might be a way to do your own live stream using Windows Media Streaming Services, using a 3g card and a “relay” server with enough upload bandwidth for all your viewers.

Normal people would probably be better off with www.ustream.tv,  justin.tv,  www.livestream.com or something.

I haven’t tried this lately, it’s from an email I wrote in march. (I don’t have anything new to ramble about yet today)

Two machines:

  • Broadcast:
  • Relay/Server:
    • Running Windows Server
    • Has the “Streaming Media Server” role installed
    • Is not behind a firewall, or is behind a firewall you can configure. In my case the server is behind my firewall on my cable internet connection, I’ll discuss configuring the firewall/router below.
    • Know the public address that is forwarded to your server. My examples below will be 76.89.123.45
    • Know the internal address behind the firewall of your server. My examples below will be 192.168.1.50
    • You’ll need a login on the server… I used an administrator login with my name

Relay/Server configuration:

  1. Enable “WMS HTTP Server Control Protocol”
  2. Start the “Windows Media Services” administration tool
  3. Select your server in the tree on the left
  4. Go to the properties tab on the right
  5. Select control protocol in the Category payne
  6. On the plug-in payne right click on “WMS HTTP Server Control Protocol”, and choose properties
  7. Change the port to something besides 80 if you have IIS on this machine. I chose 13579
  8. Close this dialog
  9. Right click “WMS HTTP Server Control Protocol” and choose “Enable”

Firewall configuration:

  1. Port forwarding (to your server, my example is 192.168.1.50
  2. From port 554 to 192.168.1.50 port 554, on both tcp and udp (or at least tcp if you don’t have an option for both). This is for mms://
  3. From port 13579 to 192.168.1.50 port 13579 over tcp (your “WMS HTTP Server Control Protocol”)

Expression Encoder:

  1. Choose a video device and so-on as usual
  2. Choose or build an encoding configuration suitable for your amount of upload bandwidth (the DSL 256k preset was more than double what my dsl can upload, you will want to experiment)
  3. On the output tab check the streaming box
  4. Choose the publishing point radio button
  5. The location I entered was http://76.89.123.45:13579/LiveStream (I chose LiveStream just now, I hadn’t entered it anywhere previously. Notice how I entered the “WMS Http Server Control Protocol” port)
  6. Click the Pre Connect button. You should be prompted for a login, I used an administrator login on the server
  7. Now, if you have access to the server, you’ll notice a new publishing point named LiveStream appeared in the “Windows Media Services” administration tool, this is good
  8. When you’re ready, click the fancy Start button in expression encoder. The stream should now be available at mms://76.89.123.45/LiveStream

Good luck!

Leave a comment