i'm try to implement rtmpt fallback with rtmp , but it doesn't work.
I can force rtmp or rtmpt in the player, but it can be better to just use rtmp, and if a user cannot read it (proxy by exemple), the player try automaticly rtmpt.
for now i use port 80 for RTMP with RED5 but i understand it is not as good as RTMPT for "application firewalls" or firewalls that "understand" the packets?
here is my rtmp fallback implementation, I think it can be used "as is".
You only have to join your streamer urls separated with a pipe (|) (rtmp://url/app|rtmpt://url2/app ...)
bc.. /** * Wrapper for playback of video streamed over RTMP. * * All playback functionalities are cross-server (FMS, Wowza, Red5), with the exception of: * - The SecureToken functionality (Wowza). * - The FCSubscribe functionality (Limelight/Akamai FMS). * - getStreamLength / checkBandwidth (FMS3). **/ package com.jeroenwijering.models {
public class RTMPModel implements ModelInterface {
/** reference to the model. **/ private var model:Model; /** Video object to be instantiated. **/ private var video:Video; /** NetConnection object for setup of the video stream. **/ private var connection:NetConnection; /** NetStream instance that handles the stream IO. **/ private var stream:NetStream; /** Sound control object. **/ private var transform:SoundTransform; /** Interval ID for the time. **/ private var timeinterval:Number; /** Timeout ID for live stream subscription pings. **/ private var timeout:Number; /** Metadata have been received. **/ private var metadata:Boolean;
/** NetConnection connection timer. **/ private var ncConnectTimer:Number = 0; /** NetConnection connection timeout (ms). **/ private var ncConnectTimeout:Number = 3000; // 3 seconds /** NetConnection connection try. **/ private var ncTryIndex:Number = 0; /** Streamer list (splited string, each streamer is separated with a pipe |). **/ private var ncStreamers:Array = new Array();
/** Constructor; sets up the connection and display. **/ public function RTMPModel(mod:Model):void { model = mod; connection = new NetConnection(); connection.addEventListener(NetStatusEvent.NET_STATUS,statusHandler); connection.addEventListener(SecurityErrorEvent.SECURITY_ERROR,errorHandler); connection.addEventListener(IOErrorEvent.IO_ERROR,errorHandler); connection.addEventListener(AsyncErrorEvent.ASYNC_ERROR,metaHandler); connection.objectEncoding = ObjectEncoding.AMF0; connection.client = new NetClient(this); video = new Video(320,240); quality(model.config['quality']); transform = new SoundTransform(); model.config['mute'] == true ? volume(0): volume(model.config['volume']);
/** Change the smoothing mode. **/ public function seek(pos:Number):void { clearTimeout(timeout); clearInterval(timeinterval); if(model.config['state'] == ModelStates.PAUSED) { stream.resume(); } else { model.sendEvent(ModelEvent.STATE,{newstate:ModelStates.PLAYING}); } stream.seek(pos); };
/** Set streaming object **/ public function setStream():void { var url = getID(model.playlist[model.config['item']]['file']); stream = new NetStream(connection); stream.addEventListener(NetStatusEvent.NET_STATUS,statusHandler); stream.addEventListener(IOErrorEvent.IO_ERROR,errorHandler); stream.addEventListener(AsyncErrorEvent.ASYNC_ERROR,metaHandler); stream.bufferTime = model.config['bufferlength']; stream.client = new NetClient(this); video.attachNetStream(stream); stream.soundTransform = transform; stream.play(url); var res:Responder = new Responder(streamlengthHandler); connection.call("getStreamLength",res,url); connection.call("checkBandwidth",null); clearInterval(timeinterval); timeinterval = setInterval(timeHandler,100); };
/** Receive NetStream status updates. **/ private function statusHandler(evt:NetStatusEvent):void { switch (evt.info.code) { case 'NetConnection.Connect.Success': clearConnectTimer(); if(evt.info.secureToken != undefined) { connection.call("secureTokenResponse",null,TEA.decrypt(evt.info.secureToken,model.config['token'])); } if(model.config['subscribe']) { timeout = setInterval(subscribe,2000,getID(model.playlist[model.config['item']]['file'])); } else { setStream(); } break; case 'NetStream.Seek.Notify': clearInterval(timeinterval); timeinterval = setInterval(timeHandler,100); break; case 'NetConnection.Connect.Rejected': if(evt.info.ex.code == 302) { model.playlist[model.config['item']]['streamer'] = evt.info.ex.redirect; connection.connect(model.playlist[model.config['item']]['streamer']); break; } case 'NetStream.Play.StreamNotFound': model.sendEvent(ModelEvent.ERROR,{message:"Stream not found: "+model.playlist[model.config['item']]['file']}); break; case 'NetConnection.Connect.Failed': tryReconnect(); break; default: model.sendEvent(ModelEvent.META,{info:evt.info.code}); break; } };
/** Destroy the stream. **/ public function stop():void { clearInterval(timeinterval); connection.close(); if(stream) { stream.close(); } video.attachNetStream(null); };
/** Get the streamlength returned from the connection. **/ private function streamlengthHandler(len:Number):void { onData({type:'streamlength',duration:len}); };
/** Interval for the position progress **/ private function timeHandler():void { var bfr = Math.round(stream.bufferLength/stream.bufferTime*100); var pos = Math.round(stream.time*10)/10; var dur = model.playlist[model.config['item']]['duration']; if(bfr < 95 && pos < Math.abs(dur-stream.bufferTime-1)) { model.sendEvent(ModelEvent.BUFFER,{percentage:bfr}); if(model.config['state'] != ModelStates.BUFFERING) { connection.call("checkBandwidth",null); model.sendEvent(ModelEvent.STATE,{newstate:ModelStates.BUFFERING}); } } else if (model.config['state'] == ModelStates.BUFFERING) { model.sendEvent(ModelEvent.STATE,{newstate:ModelStates.PLAYING}); } model.sendEvent(ModelEvent.TIME,{position:pos}); };
/** Set the volume level. **/ public function volume(vol:Number):void { transform.volume = vol/100; if(stream) { stream.soundTransform = transform; } };
I'm not sure if it's still relevant to this thread, but if you still need an answer take a look at the bottom of this page: http://www.adobe.com/devnet/flashcom/articles/firewalls_proxy02.html
Flash automatically attempts rtmpt fallback when using the rtmp protocol (so long as your media server supports the given port/protocol - e.g. Wowza requires you to alter the VHosts.xml to listen on port 80 and/or 443 for the rtmpt). No custom code is required
@Jeroen, I just did some rough testing with a stopwatch and a program called Active Ports and it appears the connections timeout after about 20-25 seconds so by the time the Flash Player has tried RTMP ports 1935, 443, and then 80 and gets to try RTMPT over port 80, more than a minute has passed. I can't imagine any user watching a spinning wheel for a minute waiting for a video. Also, if you specify a port in the RTMP URL, there is no automatic fallback at all in the Flash Player in these cases.
It would be a nice addition to JW Player if it would fall back to RTMPT over port 80 when a RTMP port is specified. It would be great if you could specify a fallback/timeout value through a FlashVar as well. For example, if you're not running a webserver on your streaming server, the most reliable option, as far as accounting for incoming and outgoing firewalls, is to run your streaming server on port 80. However, if you specify :80 in your RTMP URL, no fallback to RTMPT occurs.
We are trying to get this to work for us but have had no luck.
We use the Wowza Server and I have read the post.
1. How do we set up the code in the JW Player
2. Do we need to add code to the JW Player
3. Also, if anyone is familar with Wowza, what section of the VHost.xml needs editing
Here is what we are using right now: bc.. <script type="text/javascript"> var s1 = new SWFObject("player.swf","ply","640","381","9","#FFFFFF"); s1.addParam("allowfullscreen","true"); s1.addParam("allowscriptaccess","always"); s1.addParam("flashvars","fullscreen=true&bufferlength=2&streamer=rtmp://wowzaserver/vod&file=test/brunswick3.flv"); s1.write("container"); </script>
Jeroen, in ticket #311 you reference an older KB article which says the order is rtmp:1935, rtmp:443, rtmp:80, rtmpt:80. In practice, this doesn't work very well - we use FAVideo and know of corporate networks where you can't get video at all or only after a 45 sec delay if the firewall doesn't explicitly reject the connections. This is why NCManager (VideoPlayer, used in FLVPlayback) connects in the order rtmp:1935, rtmp:443, rtmpt:80, rtmps:443. These are opened in parallel (with a short delay) and the first connection that succeeds is used.
Fore more details, the source is here: http://opensource.adobe.com/svn/opensource/flex/sdk/trunk/frameworks/projects/framework/src/mx/controls/videoClasses/NCManager.as
Hey, I took Christian's code & used it for a video publisher, it was pretty easy to do and works very well.
As suggested by JW I had a look at making a plugin which did this, but couldn't see how to hook in to the connect() event and run that logic through the NCManager instead of default flash NC ..... so am hacking into the player ... any suggestions as to which event (if any) suitable to hook in to and do this, please let me know nick.p.doyle at gmail dot com cheers Nick
Also - Christian's code tries the methods almost in parallel, so is very fast to find which one works ... (it's also quite simple & elegant - nice one dude)
has the issue of rtmpt been solved regarding the delay. We are tunneling and find that there is a 6 moniute delate before JW player actually plays the video. Is there any way of connecting directly to port 80 ? We are using FMS.
I've just been bitten by this issue. We're using an rtmp stream that our client cannot view behind their firewall. Changing the URL to rtmpt on port 80 makes it work.
Like Nick Doyle above, I tried to see if there was a way to override the RTMPModel class with a smarter one and have a plugin plug it in it at runtime. It looks like the Model object is off limits to plugins...everything is declared private so there's no way I can see to access it from the View.
Is there a path here for a plugin developer that I'm missing? Or si there a change that can be made in a future version of the player that would allow replacement or extension of the Model objects?
Running into the same problem. Since I have my webserver running on port 80, I have Wowza configured on port 1935. One of our users is behind a firewall that is blocking this. How can I get it to play? I've tried this but get errors:
We haven’t gotten any volunteers to build this plugin. If anyone’s interested in writing this plugin, please contact plugins [at] longtailvideo [dot] com.
We’ve gotten a fair bit of requests for it now, so we’ll go ahead and implement such a fallback in the player. I underestimated the need for it.
There’s already a <a href="http://developer.longtailvideo.com/trac/ticket/951">ticket in the developer site</a> for the next release. Please let Pablo and me know if you have any specific wishes or quirks you want to bring to our attention.
I think I got it running by following Flowplayer implementation. Here are the functions I changed (against JW player 5.0 as I have problems displaying messages with latest versions). RTMPMediaProvider.as : bc.. /** * Wrapper for playback of _video streamed over RTMP. * * All playback functionalities are cross-server (FMS, Wowza, Red5), with the exception of: * - The SecureToken functionality (Wowza). * - getStreamLength / checkBandwidth (FMS3). **/ package com.longtailvideo.jwplayer.media { import com.jeroenwijering.events.*; import com.longtailvideo.jwplayer.events.MediaEvent; import com.longtailvideo.jwplayer.model.PlayerConfig; import com.longtailvideo.jwplayer.model.PlaylistItem; import com.longtailvideo.jwplayer.player.PlayerState; import com.longtailvideo.jwplayer.utils.NetClient; import com.longtailvideo.jwplayer.utils.TEA;
public class RTMPMediaProvider extends MediaProvider { /** Video object to be instantiated. **/ protected var _video:Video; /** NetConnection object for setup of the _video _stream. **/ protected var _connection:NetConnection;
private var _connector1:Connector; private var _connector2:Connector; private var _connectionClient:Object;
/** Loader instance that loads the XML file. **/ private var _loader:URLLoader; /** NetStream instance that handles the _stream IO. **/ protected var _stream:NetStream; /** Sound control object. **/ protected var _transformer:SoundTransform; /** Save the location of the XML redirect. **/ private var _smil:String; /** Save that the _video has been _started. **/ protected var _started:Boolean; /** ID for the position _positionInterval. **/ protected var _positionInterval:Number; /** Save that a file is _unpublished. **/ protected var _unpublished:Boolean; /** Whether the buffer has filled **/ private var _bufferFull:Boolean;
public function RTMPMediaProvider() { super('rtmp'); }
/** Constructor; sets up the connection and display. **/ public override function initializeMediaProvider(cfg:PlayerConfig):void { super.initializeMediaProvider(cfg); // _connection = new NetConnection(); // _connection.addEventListener(NetStatusEvent.NET_STATUS, statusHandler); // _connection.addEventListener(SecurityErrorEvent.SECURITY_ERROR, errorHandler); // _connection.addEventListener(IOErrorEvent.IO_ERROR, errorHandler); // _connection.addEventListener(AsyncErrorEvent.ASYNC_ERROR, errorHandler); // _connection.objectEncoding = ObjectEncoding.AMF0; // _connection.client = new NetClient(this); _connectionClient = new NetClient(this); _loader = new URLLoader(); _loader.addEventListener(Event.COMPLETE, loaderHandler); _loader.addEventListener(SecurityErrorEvent.SECURITY_ERROR, errorHandler); _loader.addEventListener(IOErrorEvent.IO_ERROR, errorHandler); _video = new Video(320, 240); _video.smoothing = config.smoothing; _transformer = new SoundTransform(); }
bc.. /** Finalizes the loading process **/ private function finishLoad():void { var ext:String = item.file.substr(-4); if (ext == '.mp3'){ media = null; } else if (!media) { media = _video; } var parts:Array = getUrlParts(item.streamer); _connector1 = new Connector((parts[0] == 'rtmp' ? 'rtmp' : 'rtmpe') + '://' + parts[1], _connectionClient, onConnectorSuccess, onConnectorFailure); _connector2 = new Connector((parts[0] == 'rtmp' ? 'rtmpt' : 'rtmpte') +'://' + parts[1], _connectionClient, onConnectorSuccess, onConnectorFailure);
// RTMPT connect is started after 250 ms var delay:Timer = new Timer( 250, 1); delay.addEventListener(TimerEvent.TIMER, function(event:TimerEvent):void { doConnect(_connector2, "best", ObjectEncoding.AMF0); }); delay.start();
public class Connector { private var _url:String; private var _successListener:Function; private var _connectionClient:Object; private var _connection:NetConnection; private var _failureListener:Function; private var _failed:Boolean;
public function Connector(url:String, connectionClient:Object, onSuccess:Function, onFailure:Function) { _url = url; _connectionClient = connectionClient; _successListener = onSuccess; _failureListener = onFailure; _failed = false; Logger.log("created with connection client " + _connectionClient); }
public function connect(proxyType:String, objectEncoding:uint, connectionArgs:Array):void { Logger.log(this +"::connect() using proxy type '" + proxyType + "'" + ", object encoding " + objectEncoding); if (_successListener == null) { Logger.log(this + ", this connector has been stopped, will not proceed with connect()"); return; } _connection = new NetConnection(); _connection.proxyType = proxyType; _connection.objectEncoding = objectEncoding;
I was wondering if protocol fallback has been implemented in jwplayer yet?
We are using jwplayer 5.2 at the moment (in combination with Wowza) and would like to open streams with rtmp on port 1935. But for those with stateful proxys/firewalls (or other blocks) fallback to rtmpt on port 80 would be very welcome.
This feature has been implemented in the most recent build of the player, but it has not yet been released (it will be included in the 5.3 release). If you’d like to check out a preview of the 5.3 player, you can download the most recent version at:
any ideas on when 5.3 will be released with this auto-fallback (RTMPT) feature? i too am needing this pretty quickly. as a heads up, perhaps someone can provide the necessary syntax in regards to flashvars or the streamer var to make the fallback work?