PageRenderTime 54ms CodeModel.GetById 18ms RepoModel.GetById 1ms app.codeStats 0ms

/src/actionscript/happyworm/jPlayer/JplayerRtmp.as

http://github.com/happyworm/jPlayer
ActionScript | 992 lines | 739 code | 129 blank | 124 comment | 151 complexity | 56841d8b9c21b6c414cf16247f9db31c MD5 | raw file
  1. /*
  2. * jPlayer Plugin for jQuery JavaScript Library
  3. * http://www.jplayer.org
  4. *
  5. * Copyright (c) 2009 - 2014 Happyworm Ltd
  6. * Licensed under the MIT license.
  7. * http://opensource.org/licenses/MIT
  8. *
  9. * Author: Robert M. Hall
  10. * Date: 20th November 2014
  11. * Based on JplayerMp4.as with modifications for rtmp
  12. */
  13. package happyworm.jPlayer
  14. {
  15. import flash.display.Sprite;
  16. import flash.media.Video;
  17. import flash.media.SoundTransform;
  18. import flash.net.NetConnection;
  19. import flash.net.NetStream;
  20. import flash.net.Responder;
  21. import flash.utils.Timer;
  22. import flash.utils.getTimer;
  23. import flash.events.NetStatusEvent;
  24. import flash.events.SecurityErrorEvent;
  25. import flash.events.TimerEvent;
  26. import flash.events.ErrorEvent;
  27. import flash.events.UncaughtErrorEvent;
  28. import flash.utils.clearInterval;
  29. import flash.utils.setInterval;
  30. import happyworm.jPlayer.ConnectManager;
  31. public class JplayerRtmp extends Sprite
  32. {
  33. public var myVideo:Video = new Video;
  34. private var myConnection:NetConnection;
  35. private var myStream:NetStream;
  36. public var responder:Responder;
  37. private var streamName:String;
  38. private var connectString:Object;
  39. private var firstTime:Boolean = true;
  40. private var myTransform:SoundTransform = new SoundTransform ;
  41. public var myStatus:JplayerStatus = new JplayerStatus ;
  42. private var ConnMgr:ConnectManager=new ConnectManager();
  43. private var timeUpdateTimer:Timer = new Timer(250,0);// Matched to HTML event freq
  44. private var progressTimer:Timer = new Timer(250,0);// Matched to HTML event freq
  45. private var seekingTimer:Timer = new Timer(100,0);// Internal: How often seeking is checked to see if it is over.
  46. private var startBuffer:Number = 3;
  47. private var maxBuffer:Number = 12;
  48. public function JplayerRtmp(volume:Number)
  49. {
  50. myConnection = new NetConnection ;
  51. myConnection.client = this;
  52. // Moved the netconnection negotiation into the ConnectManager.as class - not needed for initial connection
  53. // may need to add eventHandler back in for errors only or just dispatch from the ConnectManager..revisit...
  54. // myConnection.addEventListener(NetStatusEvent.NET_STATUS,netStatusHandler);
  55. // myConnection.addEventListener(SecurityErrorEvent.SECURITY_ERROR,securityErrorHandler);
  56. myVideo.smoothing = true;
  57. this.addChild(myVideo);
  58. timeUpdateTimer.addEventListener(TimerEvent.TIMER,timeUpdateHandler);
  59. progressTimer.addEventListener(TimerEvent.TIMER,progressHandler);
  60. seekingTimer.addEventListener(TimerEvent.TIMER,seekingHandler);
  61. myStatus.volume = volume;
  62. addEventListener(UncaughtErrorEvent.UNCAUGHT_ERROR, uncaughtErrorHandler);
  63. }
  64. private function uncaughtErrorHandler(event:UncaughtErrorEvent):void
  65. {
  66. trace("UNCAUGHT ERROR - try loading again");
  67. if (event.error is Error)
  68. {
  69. var error:Error = event.error as Error;
  70. trace(error);
  71. // do something with the error
  72. }
  73. else if (event.error is ErrorEvent)
  74. {
  75. var errorEvent:ErrorEvent = event.error as ErrorEvent;
  76. // do something with the error
  77. trace(errorEvent);
  78. }
  79. else
  80. {
  81. // a non-Error, non-ErrorEvent type was thrown and uncaught
  82. }
  83. load();
  84. }
  85. private function progressUpdates(active:Boolean):void
  86. {
  87. if (active)
  88. {
  89. progressTimer.start();
  90. }
  91. else
  92. {
  93. progressTimer.stop();
  94. }
  95. }
  96. private function progressHandler(e:TimerEvent):void
  97. {
  98. if (myStatus.isLoading)
  99. {
  100. if ((getLoadRatio() == 1))
  101. {// Close as can get to a loadComplete event since client.onPlayStatus only works with FMS
  102. this.dispatchEvent(new JplayerEvent(JplayerEvent.DEBUG_MSG,myStatus,"progressHandler: loadComplete"));
  103. myStatus.loaded();
  104. progressUpdates(false);
  105. }
  106. }
  107. progressEvent();
  108. }
  109. private function progressEvent():void
  110. {
  111. // temporarily disabled progress event dispatching - not really needed for rtmp
  112. //this.dispatchEvent(new JplayerEvent(JplayerEvent.DEBUG_MSG,myStatus,"progressEvent:"));
  113. updateStatusValues();
  114. this.dispatchEvent(new JplayerEvent(JplayerEvent.JPLAYER_PROGRESS,myStatus));
  115. }
  116. private function timeUpdates(active:Boolean):void
  117. {
  118. if (active)
  119. {
  120. timeUpdateTimer.start();
  121. }
  122. else
  123. {
  124. timeUpdateTimer.stop();
  125. }
  126. }
  127. private function timeUpdateHandler(e:TimerEvent):void
  128. {
  129. timeUpdateEvent();
  130. }
  131. private function timeUpdateEvent():void
  132. {
  133. updateStatusValues();
  134. this.dispatchEvent(new JplayerEvent(JplayerEvent.JPLAYER_TIMEUPDATE,myStatus));
  135. }
  136. private function seeking(active:Boolean):void
  137. {
  138. if (active)
  139. {
  140. if (! myStatus.isSeeking)
  141. {
  142. seekingEvent();
  143. }
  144. seekingTimer.start();
  145. }
  146. else
  147. {
  148. if (myStatus.isSeeking)
  149. {
  150. seekedEvent();
  151. }
  152. seekingTimer.stop();
  153. }
  154. }
  155. private function seekingHandler(e:TimerEvent):void
  156. {
  157. if ((getSeekTimeRatio() <= getLoadRatio()))
  158. {
  159. seeking(false);
  160. if (myStatus.playOnSeek)
  161. {
  162. myStatus.playOnSeek = false;// Capture the flag.
  163. play(myStatus.pausePosition);// Must pass time or the seek time is never set.
  164. }
  165. else
  166. {
  167. pause(myStatus.pausePosition);// Must pass time or the stream.time is read.
  168. }
  169. }
  170. else if (myStatus.metaDataReady && myStatus.pausePosition > myStatus.duration)
  171. {
  172. // Illegal seek time
  173. seeking(false);
  174. pause(0);
  175. }
  176. }
  177. private function seekingEvent():void
  178. {
  179. myStatus.isSeeking = true;
  180. updateStatusValues();
  181. this.dispatchEvent(new JplayerEvent(JplayerEvent.JPLAYER_SEEKING,myStatus));
  182. }
  183. private function seekedEvent():void
  184. {
  185. myStatus.isSeeking = false;
  186. updateStatusValues();
  187. this.dispatchEvent(new JplayerEvent(JplayerEvent.JPLAYER_SEEKED,myStatus));
  188. }
  189. private function netStatusHandler(e:NetStatusEvent):void
  190. {
  191. trace(("netStatusHandler: " + e.info.code));
  192. //this.dispatchEvent(new JplayerEvent(JplayerEvent.DEBUG_MSG,myStatus,"netStatusHandler: '" + e.info.code + "'"));
  193. //trace("BEFORE: bufferTime: "+myStream.bufferTime+" - bufferTimeMax: "+myStream.bufferTimeMax);
  194. switch (e.info.code)
  195. {
  196. case "NetConnection.Connect.Success" :
  197. // connectStream(); // This method did not do anything sensible anyway.
  198. // Do not think this case occurs. This was for the static file connection.
  199. // Which now seems to be handled by the Connection Manager.
  200. break;
  201. case "NetStream.Buffer.Full":
  202. if(connectString.streamTYPE == "LIVE") {
  203. myStream.bufferTime = startBuffer;
  204. } else {
  205. myStream.bufferTime = maxBuffer;
  206. }
  207. break;
  208. case "NetStream.Buffer.Flush":
  209. myStream.bufferTime = startBuffer;
  210. break;
  211. case "NetStream.Buffer.Empty":
  212. myStream.bufferTime = startBuffer;
  213. break;
  214. case "NetStream.Seek.Notify":
  215. myStream.bufferTime = startBuffer;
  216. break;
  217. case "NetStream.Play.Start" :
  218. if (firstTime) {
  219. firstTime = false; // Capture flag
  220. myStatus.loading();
  221. this.dispatchEvent(new JplayerEvent(JplayerEvent.JPLAYER_LOADSTART,myStatus));
  222. // NB: With MP4 player both audio and video types get connected to myVideo.
  223. // NB: Had believed it was important for the audio too, otherwise what plays it?
  224. if(videoBinding) {
  225. myVideo.attachNetStream(myStream);
  226. }
  227. setVolume(myStatus.volume);
  228. // Really the progress event just needs to be generated once, and should probably happen before now.
  229. progressUpdates(true);
  230. // This is an ASSUMPTION! Made it so that the default GUI worked.
  231. // Hence why this part should be refactored.
  232. // Lots of commands sequences after setMedia would be corrupted by this assumption.
  233. // Bascally, we assume that after a setMedia, you will play it.
  234. // Doing setMedia and pause(15) cause the flag to be set incorrectly and the GUI locks up.
  235. myStatus.isPlaying = true; // Should be handled elsewhere.
  236. }
  237. // Under RTMP, this event code occurs every time the media starts playing and when a new position is seeked to, even when paused.
  238. // Since under RTMP the event behaviour is quite different, believe a refactor is best here.
  239. // ie., Under RTMP we should be able to know a lot more info about the stream.
  240. // See onMetaDataHandler() for other condition, since duration is vital.
  241. // See onResult() response handler too.
  242. // Appears to be some duplication between onMetaDataHandler() and onResult(), along with a race between them occuring.
  243. break;
  244. case "NetStream.Play.UnpublishNotify":
  245. myStream.bufferTime = startBuffer; // was 3
  246. case "NetStream.Play.Stop" :
  247. myStream.bufferTime = startBuffer; // was 3
  248. //this.dispatchEvent(new JplayerEvent(JplayerEvent.DEBUG_MSG,myStatus,"NetStream.Play.Stop: getDuration() - getCurrentTime() = " + (getDuration() - getCurrentTime())));
  249. // Check if media is at the end (or close) otherwise this was due to download bandwidth stopping playback. ie., Download is not fast enough.
  250. if (Math.abs((getDuration() - getCurrentTime())) < 150)
  251. {// Testing found 150ms worked best for M4A files, where playHead(99.9) caused a stuck state due to firing with ~116ms left to play.
  252. //endedEvent();
  253. }
  254. break;
  255. case "NetStream.Seek.InvalidTime" :
  256. // Used for capturing invalid set times and clicks on the end of the progress bar.
  257. endedEvent();
  258. break;
  259. case "NetStream.Play.StreamNotFound" :
  260. myStatus.error();
  261. // Resets status except the src, and it sets srcError property.;
  262. this.dispatchEvent(new JplayerEvent(JplayerEvent.JPLAYER_ERROR,myStatus));
  263. break;
  264. }
  265. //trace("AFTER: bufferTime: "+myStream.bufferTime+" - bufferTimeMax: "+myStream.bufferTimeMax);
  266. // "NetStream.Seek.Notify" event code is not very useful. It occurs after every seek(t) command issued and does not appear to wait for the media to be ready.
  267. }
  268. private function endedEvent():void
  269. {
  270. trace("ENDED STREAM EVENT");
  271. var wasPlaying:Boolean = myStatus.isPlaying;
  272. // timeUpdates(false);
  273. // timeUpdateEvent();
  274. pause(0);
  275. if (wasPlaying)
  276. {
  277. this.dispatchEvent(new JplayerEvent(JplayerEvent.JPLAYER_ENDED,myStatus));
  278. }
  279. }
  280. private function securityErrorHandler(event:SecurityErrorEvent):void
  281. {
  282. //this.dispatchEvent(new JplayerEvent(JplayerEvent.DEBUG_MSG,myStatus,"securityErrorHandler."));
  283. }
  284. public function connectStream():void
  285. {
  286. trace("CONNECTING");
  287. //this.dispatchEvent(new JplayerEvent(JplayerEvent.DEBUG_MSG,myStatus,"connectStream."));
  288. //this.dispatchEvent(new JplayerEvent(JplayerEvent.JPLAYER_CANPLAY,myStatus));
  289. timeUpdates(true);
  290. progressUpdates(true);
  291. //myVideo.attachNetStream(myStream);
  292. //setVolume(myStatus.volume);
  293. }
  294. private function onResult(result:Object):void
  295. {
  296. trace("OnResult EVENT FIRED!");
  297. myStatus.duration = parseFloat(result.toString()) * 1000;
  298. trace((("The stream length is " + result) + " seconds"));
  299. if(!myConnection.connected) {
  300. load();
  301. } else {
  302. //this.dispatchEvent(new JplayerEvent(JplayerEvent.JPLAYER_CANPLAY,myStatus,"Rockit!"));
  303. //myStatus.loaded();
  304. //myStatus.isPlaying=true;
  305. if (! myStatus.metaDataReady)
  306. {
  307. //this.dispatchEvent(new JplayerEvent(JplayerEvent.DEBUG_MSG,myStatus,"onMetaDataHandler: " + myStatus.duration));
  308. // Allow multiple onResult Handlers to affect size. As per PR #131 and #98.
  309. // myStatus.metaDataReady = true;
  310. /*var info:Object = new Object();
  311. info.duration=myStatus.duration
  312. info.width=undefined;
  313. info.height=undefined;
  314. myStatus.metaData = info;
  315. */
  316. if (myStatus.playOnLoad)
  317. {
  318. myStatus.playOnLoad = false;// Capture the flag
  319. if (myStatus.pausePosition > 0)
  320. {// Important for setMedia followed by play(time).
  321. play(myStatus.pausePosition);
  322. }
  323. else
  324. {
  325. play();// Not always sending pausePosition avoids the extra seek(0) for a normal play() command.
  326. }
  327. }
  328. else
  329. {
  330. pause(myStatus.pausePosition);// Always send the pausePosition. Important for setMedia() followed by pause(time). Deals with not reading stream.time with setMedia() and play() immediately followed by stop() or pause(0)
  331. }
  332. this.dispatchEvent(new JplayerEvent(JplayerEvent.JPLAYER_LOADEDMETADATA,myStatus));
  333. }
  334. else
  335. {
  336. //this.dispatchEvent(new JplayerEvent(JplayerEvent.DEBUG_MSG,myStatus,"onMetaDataHandler: Already read (NO EFFECT)"));
  337. }
  338. myStream.play(streamName);
  339. this.dispatchEvent(new JplayerEvent(JplayerEvent.JPLAYER_PLAY,myStatus));
  340. // timeUpdates(false);
  341. }
  342. }
  343. private var overRideConnect:Boolean=false;
  344. public function doneYet():void {
  345. if(!myConnection.connected) {
  346. // try again
  347. ConnMgr.stopAll(true);
  348. overRideConnect=true;
  349. trace("Connected: "+myConnection.connected+" - "+myStatus.loadRequired());
  350. load();
  351. }
  352. }
  353. private var videoBinding:Boolean=false;
  354. public function setFile(src:String,videoSupport:Boolean=false):void
  355. {
  356. // videoSupport turns on/off video - by default no video, audio only
  357. videoBinding=videoSupport;
  358. /* Dont close the stream or netconnection here anymore so we can recycle if host/appname are the same
  359. if ((myStream != null))
  360. {
  361. myStream.close();
  362. myConnection.close();
  363. }
  364. */
  365. if(ConnMgr.getNegotiating() == true) {
  366. //ConnMgr.stopAll();
  367. ConnMgr.setNegotiating(false);
  368. }
  369. myVideo.clear();
  370. progressUpdates(false);
  371. timeUpdates(false);
  372. myStatus.reset();
  373. myStatus.src = src;
  374. myStatus.srcSet = true;
  375. firstTime = true;
  376. //myStatus.loaded();
  377. if(src != "") {
  378. this.dispatchEvent(new JplayerEvent(JplayerEvent.JPLAYER_CANPLAY,myStatus));
  379. }
  380. //timeUpdateEvent();
  381. }
  382. public function shutDownNcNs():void {
  383. trace("Connections Closed");
  384. timeUpdates(false);
  385. progressUpdates(false);
  386. myStream.close();
  387. myConnection.close();
  388. myStatus.reset();
  389. this.dispatchEvent(new JplayerEvent(JplayerEvent.JPLAYER_ENDED,myStatus));
  390. }
  391. public function clearFile():void
  392. {
  393. if (myStream != null)
  394. {
  395. myStream.close();
  396. // Dont close the netConnection here any longer, as we may recycle it later
  397. // may need an extra way to close manually if switching media types after an rtmp session - revisit
  398. // myConnection.close();
  399. myStatus.reset();
  400. }
  401. setFile("");
  402. myStatus.srcSet = false;
  403. }
  404. public function parseRTMPsrcConnect(rtmpSrc:String):Object
  405. {
  406. // rtmp://cp76372.live.edgefcs.net/live/Flash1Office@60204
  407. var appName:String = "";
  408. var streamFileName:String = "";
  409. var startIndex:uint = 2 + rtmpSrc.indexOf("//");
  410. var streamTYPE:String = "recorded";
  411. var host:String = rtmpSrc.substr(startIndex);
  412. var port:String = "";
  413. host = host.substr(0,host.indexOf("/"));
  414. var endHost:Number = startIndex + host.length + 1;
  415. // See if there is a host port specified
  416. if(host.indexOf(":") != -1) {
  417. port = host.substr(host.indexOf(":")+1);
  418. host = host.substr(0,host.indexOf(":"));
  419. }
  420. // Akamai specific live streams
  421. if (rtmpSrc.lastIndexOf("/live/") != -1)
  422. {
  423. trace("LIVE!");
  424. appName = rtmpSrc.substring(endHost,rtmpSrc.lastIndexOf("/live/") + 6);
  425. streamFileName = rtmpSrc.substr(rtmpSrc.lastIndexOf("/live/") + 6);
  426. streamTYPE="LIVE";
  427. } else {
  428. streamTYPE="RECORDED";
  429. }
  430. // Mp3 streams with standard appname/no instance name, mp3: prefix
  431. if (rtmpSrc.indexOf("mp3:") != -1) {
  432. appName = rtmpSrc.substring(endHost,rtmpSrc.indexOf("mp3:"));
  433. streamFileName = rtmpSrc.substr(rtmpSrc.indexOf("mp3:"));
  434. if ( streamFileName.indexOf("?") != -1 ) {
  435. var tmp:String = streamFileName.substring(streamFileName.indexOf("?")) ;
  436. streamFileName = streamFileName.substr(0,streamFileName.indexOf("?")) + encodeURI(tmp) ;
  437. } else {
  438. streamFileName = streamFileName.substr(0,streamFileName.length - 4);
  439. }
  440. }
  441. // rtmp://cp83813.edgefcs.net/ondemand/rob_hall/bruce_campbell_oldspice.flv
  442. // Mp4 streams with standard appname/no instance name, mp4: prefix
  443. if (rtmpSrc.indexOf("mp4:") != -1) {
  444. appName = rtmpSrc.substring(endHost,rtmpSrc.indexOf("mp4:"));
  445. streamFileName = rtmpSrc.substr(rtmpSrc.indexOf("mp4:"));
  446. if ( streamFileName.indexOf("?") != -1 ) {
  447. var tmpV:String = streamFileName.substring(streamFileName.indexOf("?")) ;
  448. streamFileName = streamFileName.substr(0,streamFileName.indexOf("?")) + encodeURI(tmpV) ;
  449. } else {
  450. streamFileName = streamFileName.substr(0,streamFileName.length - 4);
  451. }
  452. }
  453. // .f4v streams with standard appname/no instance name, .flv extension
  454. if (rtmpSrc.indexOf(".flv") != -1)
  455. {
  456. // allow use of ^ in rtmp string to indicate break point for an appname or instance name that
  457. // contains a / in it where it would require multiple connection attempts or manual configuratiom
  458. // of the appname/instancename
  459. var endApp:Number=0;
  460. if(rtmpSrc.indexOf("^") != -1) {
  461. endApp=rtmpSrc.indexOf("^");
  462. rtmpSrc.replace("^", "/");
  463. } else {
  464. endApp = rtmpSrc.indexOf("/",endHost);
  465. }
  466. appName = rtmpSrc.substring(endHost,endApp) + "/";
  467. streamFileName = rtmpSrc.substr(endApp+1);
  468. }
  469. if(port=="") {
  470. port="MULTI";
  471. }
  472. //rtmp, rtmpt, rtmps, rtmpe, rtmpte
  473. trace(("\n\n*** HOST: " + host));
  474. trace(("*** PORT: " + port));
  475. trace(("*** APPNAME: " + appName));
  476. trace(("*** StreamName: " + streamFileName));
  477. var streamParts:Object = new Object;
  478. streamParts.streamTYPE=streamTYPE;
  479. streamParts.appName = appName;
  480. streamParts.streamFileName = streamFileName;
  481. streamParts.hostName = host;
  482. streamName = streamFileName;
  483. return streamParts;
  484. }
  485. public function load():Boolean
  486. {
  487. //trace("LOAD: "+myStatus.src);
  488. if (myStatus.loadRequired() || overRideConnect==true)
  489. {
  490. overRideConnect=false;
  491. myStatus.startingDownload();
  492. var lastAppName:String;
  493. var lastHostName:String;
  494. try{
  495. // we do a try, as these properties might not exist yet
  496. if(connectString.appName != "" && connectString.appName != undefined) {
  497. trace("PREVIOUS APP/HOST INFO AVAILABLE");
  498. lastAppName = connectString.appName;
  499. lastHostName = connectString.hostName;
  500. trace("LAST: "+lastAppName,lastHostName);
  501. }
  502. } catch (error:Error) {
  503. //trace("*** Caught an error condition: "+error);
  504. }
  505. connectString = parseRTMPsrcConnect(myStatus.src);
  506. trace("**** LOAD :: CONNECT SOURCE: " +connectString.hostName +" "+ connectString.appName);
  507. this.dispatchEvent(new JplayerEvent(JplayerEvent.JPLAYER_WAITING, myStatus));
  508. if((connectString.appName == lastAppName && connectString.hostName == lastHostName) && (myConnection.connected)) {
  509. // recycle the netConnection
  510. trace("RECYCLING NETCONNECTION");
  511. if ((myStream != null))
  512. {
  513. myStream.close();
  514. }
  515. connectStream();
  516. onBWDone(null,myConnection);
  517. } else {
  518. // myConnection.connect(connectString.appName);
  519. trace("NEW NetConnection Negotiation");
  520. if ((myStream != null))
  521. {
  522. myStream.close();
  523. myConnection.close();
  524. }
  525. ConnMgr.stopAll(true);
  526. ConnMgr.negotiateConnect(this,connectString.hostName,connectString.appName);
  527. }
  528. trace("**** LOAD2 :: CONNECT SOURCE: " +connectString.hostName +" "+ connectString.appName);
  529. this.dispatchEvent(new JplayerEvent(JplayerEvent.JPLAYER_WAITING, myStatus));
  530. return true;
  531. }
  532. else
  533. {
  534. return false;
  535. }
  536. }
  537. public function onFCUnsubscribe(info:Object):void
  538. {
  539. trace(("onFCUnSubscribe worked" + info));
  540. }
  541. public function onFCSubscribe(info:Object):void
  542. {
  543. trace(("onFCSubscribe worked" + info));
  544. }
  545. public function onBWDone(info:Object,nc:NetConnection):void
  546. {
  547. if(nc.connected) {
  548. myConnection=nc;
  549. trace(((("onBWDone " + info) + " :: ") + myStatus.src));
  550. var customClient:Object = new Object ;
  551. customClient.onMetaData = onMetaDataHandler;
  552. customClient.onPlayStatus = onPlayStatusHandler;// According to the forums and my tests, onPlayStatus only works with FMS (Flash Media Server).
  553. myStream = null;
  554. myStream = new NetStream(myConnection);
  555. myStream.addEventListener(NetStatusEvent.NET_STATUS,netStatusHandler);
  556. myStream.client = customClient;
  557. if(connectString.streamTYPE == "LIVE") {
  558. myStream.bufferTime = 3; // was 3
  559. myStream.bufferTimeMax = 24;
  560. startBuffer = 3;
  561. maxBuffer = 12;
  562. } else {
  563. myStream.bufferTime = .2; // was 3
  564. myStream.bufferTimeMax = 0;
  565. startBuffer = .2;
  566. maxBuffer = 12;
  567. }
  568. //streamName="";
  569. //var connectString:Object = parseRTMPsrcConnect(myStatus.src);
  570. //streamName=connectString.streamFileName;
  571. responder = new Responder(onResult);
  572. myConnection.call("getStreamLength",responder,streamName);
  573. } else {
  574. connectStream();
  575. }
  576. trace("PLAY SOURCE: "+connectString);
  577. }
  578. public function play(time:Number = NaN):Boolean {
  579. //trace("PLAY: "+time+" - isPlaying: "+myStatus.isPlaying +" - myStatus.isStartingDownload:"+myStatus.isStartingDownload);
  580. var wasPlaying:Boolean = myStatus.isPlaying;
  581. if(!isNaN(time) && myStatus.srcSet) {
  582. if(myStatus.isPlaying) {
  583. myStream.pause();
  584. myStatus.isPlaying = false;
  585. }
  586. myStatus.pausePosition = time;
  587. }
  588. if(myStatus.isStartingDownload) {
  589. myStatus.playOnLoad = true; // Raise flag, captured in onMetaDataHandler()
  590. return true;
  591. } else if(myStatus.loadRequired()) {
  592. myStatus.playOnLoad = true; // Raise flag, captured in onMetaDataHandler()
  593. return load();
  594. } else if((myStatus.isLoading || myStatus.isLoaded) && !myStatus.isPlaying) {
  595. if(myStatus.metaDataReady && myStatus.pausePosition > myStatus.duration && connectString.streamTYPE != "LIVE") { // The time is invalid, ie., past the end.
  596. myStream.pause(); // Since it is playing by default at this point.
  597. myStatus.pausePosition = 0;
  598. trace("SEEKER!");
  599. myStream.seek(0);
  600. timeUpdates(false);
  601. timeUpdateEvent();
  602. if(wasPlaying) { // For when playing and then get a play(huge)
  603. this.dispatchEvent(new JplayerEvent(JplayerEvent.JPLAYER_PAUSE, myStatus));
  604. }
  605. } else if(getSeekTimeRatio() > getLoadRatio()) { // Use an estimate based on the downloaded amount
  606. myStatus.playOnSeek = true;
  607. seeking(true);
  608. trace("SEEKER PAUSE!");
  609. myStream.pause(); // Since it is playing by default at this point.
  610. } else {
  611. if(!isNaN(time)) { // Avoid using seek() when it is already correct.
  612. trace("SEEKER3");
  613. myStream.seek(myStatus.pausePosition/1000); // Since time is in ms and seek() takes seconds
  614. }
  615. myStatus.isPlaying = true; // Set immediately before playing. Could affects events.
  616. trace("SHOULD GET RESUME!");
  617. myStream.resume();
  618. timeUpdates(true);
  619. if(!wasPlaying) {
  620. this.dispatchEvent(new JplayerEvent(JplayerEvent.JPLAYER_PLAY, myStatus));
  621. }
  622. }
  623. return true;
  624. } else {
  625. return false;
  626. }
  627. }
  628. public function pause(time:Number=NaN):Boolean
  629. {
  630. //trace("PAUSE: "+time);
  631. myStatus.playOnLoad = false;// Reset flag in case load/play issued immediately before this command, ie., before onMetadata() event.
  632. myStatus.playOnSeek = false;// Reset flag in case play(time) issued before the command and is still seeking to time set.
  633. var wasPlaying:Boolean = myStatus.isPlaying;
  634. // To avoid possible loops with timeupdate and pause(time). A pause() does not have the problem.
  635. var alreadyPausedAtTime:Boolean = false;
  636. if(!isNaN(time) && myStatus.pausePosition == time) {
  637. alreadyPausedAtTime = true;
  638. }
  639. trace("!isNaN: "+!isNaN(time) +" isNaN: "+isNaN(time));
  640. // Need to wait for metadata to load before ever issuing a pause. The metadata handler will call this function if needed, when ready.
  641. if (((myStream != null) && myStatus.metaDataReady))
  642. {// myStream is a null until the 1st media is loaded. ie., The 1st ever setMedia being followed by a pause() or pause(t).
  643. if(connectString.streamTYPE == "LIVE") {
  644. trace("PAUSING LIVE");
  645. myStream.play(false)
  646. } else {
  647. trace("PAUSING RECORDED");
  648. myStream.pause();
  649. }
  650. }
  651. if (myStatus.isPlaying)
  652. {
  653. myStatus.isPlaying = false;
  654. myStatus.pausePosition = myStream.time * 1000;
  655. }
  656. if (! isNaN(time) && myStatus.srcSet)
  657. {
  658. myStatus.pausePosition = time;
  659. }
  660. if (wasPlaying)
  661. {
  662. this.dispatchEvent(new JplayerEvent(JplayerEvent.JPLAYER_PAUSE,myStatus));
  663. }
  664. if (myStatus.isStartingDownload)
  665. {
  666. return true;
  667. }
  668. else if (myStatus.loadRequired())
  669. {
  670. if ((time > 0))
  671. {// We do not want the stop() command, which does pause(0), causing a load operation.
  672. return load();
  673. }
  674. else
  675. {
  676. return true;// Technically the pause(0) succeeded. ie., It did nothing, since nothing was required.
  677. }
  678. }
  679. else if (myStatus.isLoading || myStatus.isLoaded)
  680. {
  681. if (myStatus.metaDataReady && myStatus.pausePosition > myStatus.duration && connectString.streamTYPE != "LIVE" )
  682. {// The time is invalid, ie., past the end.
  683. myStatus.pausePosition = 0;
  684. trace("GOT HERE!");
  685. myStream.seek(0);
  686. seekedEvent();// Deals with seeking effect when using setMedia() then pause(huge). NB: There is no preceeding seeking event.
  687. }
  688. else if (! isNaN(time))
  689. {
  690. if ((getSeekTimeRatio() > getLoadRatio()))
  691. {// Use an estimate based on the downloaded amount
  692. seeking(true);
  693. }
  694. else
  695. {
  696. if (myStatus.metaDataReady && connectString.streamTYPE != "LIVE")
  697. {// Otherwise seek(0) will stop the metadata loading.
  698. trace("GOT HERE TOO!");
  699. myStream.seek(myStatus.pausePosition / 1000);
  700. }
  701. }
  702. }
  703. timeUpdates(false);
  704. // Need to be careful with timeupdate event, otherwise a pause in a timeupdate event can cause a loop.
  705. // Neither pause() nor pause(time) will cause a timeupdate loop.
  706. if(wasPlaying || !isNaN(time) && !alreadyPausedAtTime) {
  707. timeUpdateEvent();
  708. }
  709. return true;
  710. }
  711. else
  712. {
  713. return false;
  714. }
  715. }
  716. public function playHead(percent:Number):Boolean
  717. {
  718. var time:Number = percent * getDuration() * getLoadRatio() / 100;
  719. if (myStatus.isPlaying || myStatus.playOnLoad || myStatus.playOnSeek)
  720. {
  721. return play(time);
  722. }
  723. else
  724. {
  725. return pause(time);
  726. }
  727. }
  728. public function setVolume(v:Number):void
  729. {
  730. myStatus.volume = v;
  731. myTransform.volume = v;
  732. if ((myStream != null))
  733. {
  734. myStream.soundTransform = myTransform;
  735. }
  736. }
  737. private function updateStatusValues():void
  738. {
  739. //myStatus.seekPercent = 100 * getLoadRatio();
  740. myStatus.seekPercent = 100;
  741. myStatus.currentTime = getCurrentTime();
  742. myStatus.currentPercentRelative = 100 * getCurrentRatioRel();
  743. myStatus.currentPercentAbsolute = 100 * getCurrentRatioAbs();
  744. myStatus.duration = getDuration();
  745. }
  746. public function getLoadRatio():Number
  747. {
  748. return 1;
  749. /*trace("LoadRatio:"+myStream.bytesLoaded, myStream.bytesTotal);
  750. if((myStatus.isLoading || myStatus.isLoaded) && myStream.bytesTotal > 0) {
  751. return myStream.bytesLoaded / myStream.bytesTotal;
  752. } else if (myStatus.isLoaded && myStream.bytesLoaded > 0) {
  753. return 1;
  754. } else {
  755. return 0;
  756. }
  757. */
  758. }
  759. public function getDuration():Number
  760. {
  761. return myStatus.duration;// Set from meta data.
  762. }
  763. public function getCurrentTime():Number
  764. {
  765. if (myStatus.isPlaying)
  766. {
  767. //trace(myStream.time * 1000);
  768. return myStream.time * 1000; // was +1000
  769. }
  770. else
  771. {
  772. return myStatus.pausePosition;
  773. }
  774. }
  775. public function getCurrentRatioRel():Number
  776. {
  777. if ((getCurrentRatioAbs() <= getLoadRatio()))
  778. {
  779. //if((getLoadRatio() > 0) && (getCurrentRatioAbs() <= getLoadRatio())) {
  780. return getCurrentRatioAbs() / getLoadRatio();
  781. }
  782. else
  783. {
  784. return 0;
  785. }
  786. }
  787. public function getCurrentRatioAbs():Number
  788. {
  789. if ((getDuration() > 0))
  790. {
  791. return getCurrentTime() / getDuration();
  792. }
  793. else
  794. {
  795. return 0;
  796. }
  797. }
  798. public function getSeekTimeRatio():Number
  799. {
  800. if ((getDuration() > 0))
  801. {
  802. return myStatus.pausePosition / getDuration();
  803. }
  804. else
  805. {
  806. return 1;
  807. }
  808. }
  809. public function onPlayStatusHandler(infoObject:Object):void
  810. {
  811. trace((("OnPlayStatusHandler called: (" + getTimer()) + " ms)"));
  812. for (var prop:* in infoObject)
  813. {
  814. trace(((("\t" + prop) + ":\t") + infoObject[prop]));
  815. }
  816. if (infoObject.code == "NetStream.Play.Complete")
  817. {
  818. endedEvent();
  819. }
  820. }
  821. public function onMetaDataHandler(info:Object):void
  822. {// Used in connectStream() in myStream.client object.
  823. // This event occurs when jumping to the start of static files! ie., seek(0) will cause this event to occur.
  824. if (! myStatus.metaDataReady)
  825. {
  826. trace("\n\n*** METADATA FIRED! ***\n\n");
  827. //this.dispatchEvent(new JplayerEvent(JplayerEvent.DEBUG_MSG,myStatus,"onMetaDataHandler: " + info.duration + " | " + info.width + "x" + info.height));
  828. myStatus.metaDataReady = true;// Set flag so that this event only effects jPlayer the 1st time.
  829. myStatus.metaData = info;
  830. myStatus.duration = info.duration * 1000;// Only available via Meta Data.
  831. if (info.width != undefined)
  832. {
  833. myVideo.width = myStatus.videoWidth = info.width;
  834. }
  835. if (info.height != undefined)
  836. {
  837. myVideo.height = myStatus.videoHeight = info.height;
  838. }
  839. if (myStatus.playOnLoad)
  840. {
  841. myStatus.playOnLoad = false;// Capture the flag
  842. if (myStatus.pausePosition > 0)
  843. {// Important for setMedia followed by play(time).
  844. play(myStatus.pausePosition);
  845. }
  846. else
  847. {
  848. play();// Not always sending pausePosition avoids the extra seek(0) for a normal play() command.
  849. }
  850. }
  851. else
  852. {
  853. pause(myStatus.pausePosition);// Always send the pausePosition. Important for setMedia() followed by pause(time). Deals with not reading stream.time with setMedia() and play() immediately followed by stop() or pause(0)
  854. }
  855. this.dispatchEvent(new JplayerEvent(JplayerEvent.JPLAYER_LOADEDMETADATA,myStatus));
  856. }
  857. else
  858. {
  859. //this.dispatchEvent(new JplayerEvent(JplayerEvent.DEBUG_MSG,myStatus,"onMetaDataHandler: Already read (NO EFFECT)"));
  860. }
  861. }
  862. }
  863. }