msgbartop
Various ramblings-on, mostly about Red5
msgbarbottom

19 Jul 11 Dynamic streaming with Red5

I will dub this feature beta only because I’m not sure that my handling or signaling is “exactly” right yet. The latest revision is now 4245 and those of you with the skills may use this version to stream like the pros do with FMS and Wowza.. dynamically! Be aware that Q0S is not implemented yet so hold your bugs saying it doesn’t work for now.  The following transitions are currently supported:

  • NetStreamPlayTransitions.RESET – Clears any previous play calls and plays the specified stream immediately.
  • NetStreamPlayTransitions.APPEND – Adds the stream to a playlist and begins playback with the first stream.
  • NetStreamPlayTransitions.APPEND_AND_WAIT – Builds a playlist without starting to play it from the first stream.
  • NetStreamPlayTransitions.SWITCH – Switches from playing one stream to another stream, typically with streams of the same content.
  • NetStreamPlayTransitions.SWAP – Replaces a content stream with a different content stream and maintains the rest of the playlist.
  • NetStreamPlayTransitions.STOP – Stops playing the streams in a playlist.

With any new feature there will be bugs, so bare with us and we’ll get them fixed up as soon as possible. Post any issues (with test code if you have some) to the issue tracker.

Lastly, without the help of Dan Rossi and Abhinav Kapoor’s post (http://www.adobe.com/devnet/flashmediaserver/articles/dynstream_actionscript.html) this would have been a much longer endeavor. Mucho gracias to Infrared5 for allowing me to work on this and other Red5 features. All the testing so far was accomplished with Flowplayer v3.2.6.

12 Nov 09 Native RTMPS in Red5

Red5 now supports “native” RTMPS in addition to RTMPT over SSL. To use this feature you will need to use the current trunk version until 0.9 RC3 or Final are released. A big shout-out goes to Kevin Green for providing the original patch. Using this communication channel, your data will be secured throughout the process from connection to shutdown using TLS/SSL and should provide the secure features you need until RTMPE is ready.

Red5NativeRTMPS

Red5NativeRTMPS


To use this mode in your NetConnection, you must set the proxy type to best like so:

nc = new NetConnection();
nc.client = this;
nc.proxyType = "best";

For this example I used a free opensource ssl cert provided by godaddy.

Step by step process:

1. Create your key

keytool -keysize 2048 -genkey -alias red5 -keyalg RSA -keystore keystore
Enter keystore password:
Re-enter new password:
What is your first and last name?
  [Unknown]:  ssl.red5.org
What is the name of your organizational unit?
  [Unknown]:  Dev
What is the name of your organization?
  [Unknown]:  Red5
What is the name of your City or Locality?
  [Unknown]:  Henderson
What is the name of your State or Province?
  [Unknown]:  Nevada
What is the two-letter country code for this unit?
  [Unknown]:  US
Is CN=ssl.red5.org, OU=Dev, O=Red5, L=Henderson, ST=Nevada, C=US correct?
  [no]:  yes

Enter key password for <red5>
        (RETURN if same as keystore password):

2. Create a CSR

keytool -certreq -keyalg RSA -alias red5 -file red5.csr -keystore keystore
Enter keystore password:

3. Submit your CSR to your SSL certificate provider. Godaddy process is described below.

4. After your receive your certificate, import the root cert into your keystore file

keytool -import -alias root -keystore keystore -trustcacerts -file valicert_class2_root.crt
Enter keystore password:
Certificate already exists in system-wide CA keystore under alias <valicertclass2ca>
Do you still want to add it to your own keystore? [no]:  yes
Certificate was added to keystore

5. Import the cross certificates

keytool -import -alias cross -keystore keystore -trustcacerts -file gd_cross_intermediate.crt
Enter keystore password:
Certificate was added to keystore

6. Import the intermediate certificates

keytool -import -alias intermed -keystore keystore -trustcacerts -file gd_intermediate.crt
Enter keystore password:
Certificate was added to keystore

7. Import your certificate

keytool -import -alias red5 -keystore keystore -trustcacerts -file ssl.red5.org.crt
Enter keystore password:
Certificate reply was installed in keystore

8. Setup RTMPS in your red5/conf/red5-core.xml. You may notice that some of the rtmp variables are used here, that is only for ease of setup; you could set them to whatever you prefer.

    <bean id="rtmpsMinaIoHandler"
        class="org.red5.server.net.rtmps.RTMPSMinaIoHandler">
        <property name="handler" ref="rtmpHandler" />
        <property name="codecFactory" ref="rtmpCodecFactory" />
        <property name="rtmpConnManager" ref="rtmpMinaConnManager" />
	<property name="keyStorePassword" value="${rtmps.keystorepass}" />
        <property name="keystoreFile" value="conf/keystore" />
    </bean>
    
    <bean id="rtmpsTransport" class="org.red5.server.net.rtmp.RTMPMinaTransport" init-method="start" destroy-method="stop">
        <property name="ioHandler" ref="rtmpsMinaIoHandler" />
        <property name="connectors">
            <list>
                <bean class="java.net.InetSocketAddress">
                    <constructor-arg index="0" type="java.lang.String" value="${rtmps.host}" />  
                    <constructor-arg index="1" type="int" value="${rtmps.port}" />  
                </bean>
            </list>
        </property>
        <property name="receiveBufferSize" value="${rtmp.receive_buffer_size}" />
        <property name="sendBufferSize" value="${rtmp.send_buffer_size}" />
        <property name="eventThreadsCore" value="${rtmp.event_threads_core}" />
        <property name="eventThreadsMax" value="${rtmp.event_threads_max}" />
        <property name="eventThreadsQueue" value="${rtmp.event_threads_queue}" />
        <property name="eventThreadsKeepalive" value="${rtmp.event_threads_keepalive}" />
        <property name="jmxPollInterval" value="1000" />
        <property name="tcpNoDelay" value="${rtmp.tcp_nodelay}" />
    </bean>

Additional security info can be found here
The testing player source can be found here

Tags: , ,

23 Jan 09 The dreaded 2044 error

For those of us who have encountered this error, we can understand each others pain. I searched all around and never found a solution to my particular situation. I found Renaun’s post helpful but it didn’t resolve my issue, as well as these other posts:

http://renaun.com/blog/2007/07/21/226/

http://www.lonhosford.com/lonblog/2008/03/13/flex-liveconnection-and-legacy-flash-swfs/

http://blog.kazumakzak.com/2008/11/26/flex-actionscript-project-sandbox-error-error-2044/

So here is how this all came about – I had built an AIR application using FlexBuilder, which created a LocalConnection to listen for events from a Native (C++) Windows application. The error would occur when the native application was executed prior to starting the AIR application. I tried everything to catch the error and prevent the dialog from appearing; in FlexBuilder this was especially annoying because the dialog would popup behind the IDE before the application window was visible and prevent it from becomming visble until the dialog was closed. I tried creating the LocalConnection on the following events of WindowedApplication:

initialize

addedToStage

applicationComplete

windowComplete

None of which worked, this is also the order in which these event fire by-the-way. So as a last resort, I tried the “preloader” option and BINGO! The preloader runs just before the application so it is able to catch the events comming from the native application, since it would appear that Flash starts listening too early (in my case). Now on to the code…

Add the preloader attribute to the top of your application mxml

preloader="org.gregoire.Preloader" 

Preloader.as

package org.gregoire {

	import org.gregoire.event.*;
	import org.gregoire.net.*;

	import mx.preloaders.DownloadProgressBar;	

	public class Preloader extends DownloadProgressBar {   

		private var localCon:CustomLocalConnection;
		private var srcWidth:int;
		private var srcHeight:int;		

		public function Preloader() {
			super();
			trace("Preload");
			try {
				//create local connection to provide capture source dimensions
				localCon = new CustomLocalConnection("_myBrowser");
				//setup event listeners for changes on the capture source
				localCon.addEventListener(SourceEvent.CHANGED, sourceEventHandler);
			} catch(e:Error) {
				trace("Error with (preloader) LC: " + e);
			}
		}

		private function sourceEventHandler(event:SourceEvent):void {
			trace("Preloader Source change event: " + event);
			srcWidth = event.getWidth();
			srcHeight = event.getHeight();
		}		

		public function hasDimensions():Boolean {
			return (srcWidth > 0 && srcHeight > 0);
		}

		public function getSourceWidth():int {
			return srcWidth;
		}

		public function getSourceHeight():int {
			return srcHeight;
		}

		public function unregister():void {
			localCon.removeEventListener(SourceEvent.CHANGED, sourceEventHandler);
			localCon.close();
			localCon = null;
		}

	}

}

CustomLocalConnection.as

package org.gregoire.net {

    import flash.net.LocalConnection;
    import flash.events.StatusEvent;
    import flash.events.SecurityErrorEvent;
    import org.gregoire.event.SourceEvent;

    public class CustomLocalConnection extends LocalConnection {

        public function CustomLocalConnection(connectionName:String) {
            try {
            	super();
                addEventListener(StatusEvent.STATUS, onStatus);
                addEventListener(SecurityErrorEvent.SECURITY_ERROR, onSecurityError);

                allowDomain("*");
				client = this;
                connect(connectionName);

            } catch (error:ArgumentError) {
                // server already created/connected
            }
        }

        public function onSourceSelect(sourceName:String):void {
            trace("onSourceSelect called: " + sourceName);
            var evt:SourceEvent = new SourceEvent();
            evt.setSourceName(sourceName);
            dispatchEvent(evt);
        }

        public function onSize(dimension:String):void {
            trace("onSize called: " + dimension);
            //split the dimension string into width and height
            var arr:Array = dimension.split("|", 2);
            if (arr.length >= 2) {
	            var evt:SourceEvent = new SourceEvent();
            	evt.setWidth(parseInt(arr[0]));
            	evt.setHeight(parseInt(arr[1]));
            	dispatchEvent(evt);
            } else {
            	trace("Dimension could not be properly parsed");
            }

        }

		private function onStatus(event:StatusEvent):void {
			trace("LC event: " + event);
			switch (event.level) {
               case "status":
                   trace("LocalConnection.send() succeeded");
                   break;
               case "error":
                   trace("LocalConnection.send() failed");
                   break;
        	}
		}

		private function onSecurityError(e:Error):void {
			trace("LC error: " + e);
		}		

    }
}

SourceEvent.as

package org.gregoire.event {

	import flash.events.Event;

	public class SourceEvent extends Event {

		public static const CHANGED:String = "sourceChanged";

		private var sourceName:String;
		private var width:int;
		private var height:int;

		public function SourceEvent() {
			super(CHANGED);
		}

        public function getSourceName():String {
        	return sourceName;
        }

        public function setSourceName(sourceName:String):void {
        	this.sourceName = sourceName;
        }

        public function getWidth():int {
        	return width;
        }

        public function setWidth(width:int):void {
        	this.width = width;
        }

        public function getHeight():int {
        	return height;
        }

        public function setHeight(height:int):void {
        	this.height = height;
        }

        // Override the inherited clone() method.
        override public function clone():Event {
            return new SourceEvent();
        }

		override public function toString():String{
			return "[SourceEvent] sourceName="+sourceName+",width="+width+",height="+height;
		}

	}
}

I cannot release more than that at this time, but I will try to provide more later if possible. This information should be enough to get you past the 2044 error.

Tags: , , , ,

19 Nov 08 Two reasons I love Adobe

First reason is their release of AIR 1.5 and its “run-ability” on my main system (WindowsXP x64). The OS is detected as Windows 2003 but it works, so thank you!
The second reason is their delivery on the original promise of Java (my fav dev language) to “run everywhere”. I can write code in FlexBuilder and it will run on any operating system that supports Flash Player 9 or 10, this includes 64-bit Linux! For instance I wrote an application that uses a library / project called Merapi to capture desktop screen shots using AIR and Java which works on OSX and WindowsXP; I can also be certain that it works on Linux.

Go Adobe, Go!

Tags:

12 Sep 08 RTMP load tester

I want to let everyone know about a simple RTMP load testing tool that I wrote in flex. Its really simple and allows you to rip a stream from either FMS or Red5 as quickly as possible. If you find it useful or want to add to it, let me know.

The source link is for the latest version in which I have started adding shared object testing; I could use some assistance with that part if any of you have time. The bin link is to the “old” version.

Tags: , , , ,

12 Sep 08 Using custom objects in AMF3 with Red5

I created a post about this subject almost a year ago, but there were a couple minor issues with the examples. Here I will show what eight additional months of experience can provide. The example provided here uses a custom object in Flex to pass information to and from the server, which in this case will be Red5. If one of you has an example which uses FMS on the server-side, I would be glad to include it here.
(more…)

Tags: , , , , ,


Fatal error: Call to undefined function akismet_counter() in C:\xampp\htdocs\paulgregoireblog\wp-content\themes\googlechrome\footer.php on line 9