PageRenderTime 51ms CodeModel.GetById 13ms RepoModel.GetById 0ms app.codeStats 0ms

/backend/web/filemanager/js/jPlayer/actionscript/happyworm/jPlayer/JplayerRtmp.as

https://gitlab.com/angrydevops/workerpro
ActionScript | 983 lines | 731 code | 129 blank | 123 comment | 145 complexity | 08d7a56b905c6f2c95953a33077d0f60 MD5 | raw file
  1. /*
  2. * jPlayer Plugin for jQuery JavaScript Library
  3. * http://www.jplayer.org
  4. *
  5. * Copyright (c) 2009 - 2013 Happyworm Ltd
  6. * Licensed under the MIT license.
  7. * http://opensource.org/licenses/MIT
  8. *
  9. * Author: Robert M. Hall
  10. * Date: 29th January 2013
  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. {
  433. appName = rtmpSrc.substring(endHost,rtmpSrc.indexOf("mp3:"));
  434. streamFileName = rtmpSrc.substr(rtmpSrc.indexOf("mp3:"));
  435. streamFileName = streamFileName.substr(0,streamFileName.length - 4);
  436. }
  437. // rtmp://cp83813.edgefcs.net/ondemand/rob_hall/bruce_campbell_oldspice.flv
  438. // Mp4 streams with standard appname/no instance name, mp4: prefix
  439. if (rtmpSrc.indexOf("mp4:") != -1)
  440. {
  441. appName = rtmpSrc.substring(endHost,rtmpSrc.indexOf("mp4:"));
  442. streamFileName = rtmpSrc.substr(rtmpSrc.indexOf("mp4:"));
  443. streamFileName = streamFileName.substr(0,streamFileName.length - 4);
  444. }
  445. // .f4v streams with standard appname/no instance name, .flv extension
  446. if (rtmpSrc.indexOf(".flv") != -1)
  447. {
  448. // allow use of ^ in rtmp string to indicate break point for an appname or instance name that
  449. // contains a / in it where it would require multiple connection attempts or manual configuratiom
  450. // of the appname/instancename
  451. var endApp:Number=0;
  452. if(rtmpSrc.indexOf("^") != -1) {
  453. endApp=rtmpSrc.indexOf("^");
  454. rtmpSrc.replace("^", "/");
  455. } else {
  456. endApp = rtmpSrc.indexOf("/",endHost);
  457. }
  458. appName = rtmpSrc.substring(endHost,endApp) + "/";
  459. streamFileName = rtmpSrc.substr(endApp+1);
  460. }
  461. if(port=="") {
  462. port="MULTI";
  463. }
  464. //rtmp, rtmpt, rtmps, rtmpe, rtmpte
  465. trace(("\n\n*** HOST: " + host));
  466. trace(("*** PORT: " + port));
  467. trace(("*** APPNAME: " + appName));
  468. trace(("*** StreamName: " + streamFileName));
  469. var streamParts:Object = new Object;
  470. streamParts.streamTYPE=streamTYPE;
  471. streamParts.appName = appName;
  472. streamParts.streamFileName = streamFileName;
  473. streamParts.hostName = host;
  474. streamName = streamFileName;
  475. return streamParts;
  476. }
  477. public function load():Boolean
  478. {
  479. //trace("LOAD: "+myStatus.src);
  480. if (myStatus.loadRequired() || overRideConnect==true)
  481. {
  482. overRideConnect=false;
  483. myStatus.startingDownload();
  484. var lastAppName:String;
  485. var lastHostName:String;
  486. try{
  487. // we do a try, as these properties might not exist yet
  488. if(connectString.appName != "" && connectString.appName != undefined) {
  489. trace("PREVIOUS APP/HOST INFO AVAILABLE");
  490. lastAppName = connectString.appName;
  491. lastHostName = connectString.hostName;
  492. trace("LAST: "+lastAppName,lastHostName);
  493. }
  494. } catch (error:Error) {
  495. //trace("*** Caught an error condition: "+error);
  496. }
  497. connectString = parseRTMPsrcConnect(myStatus.src);
  498. trace("**** LOAD :: CONNECT SOURCE: " +connectString.hostName +" "+ connectString.appName);
  499. this.dispatchEvent(new JplayerEvent(JplayerEvent.JPLAYER_WAITING, myStatus));
  500. if((connectString.appName == lastAppName && connectString.hostName == lastHostName) && (myConnection.connected)) {
  501. // recycle the netConnection
  502. trace("RECYCLING NETCONNECTION");
  503. if ((myStream != null))
  504. {
  505. myStream.close();
  506. }
  507. connectStream();
  508. onBWDone(null,myConnection);
  509. } else {
  510. // myConnection.connect(connectString.appName);
  511. trace("NEW NetConnection Negotiation");
  512. if ((myStream != null))
  513. {
  514. myStream.close();
  515. myConnection.close();
  516. }
  517. ConnMgr.stopAll(true);
  518. ConnMgr.negotiateConnect(this,connectString.hostName,connectString.appName);
  519. }
  520. trace("**** LOAD2 :: CONNECT SOURCE: " +connectString.hostName +" "+ connectString.appName);
  521. this.dispatchEvent(new JplayerEvent(JplayerEvent.JPLAYER_WAITING, myStatus));
  522. return true;
  523. }
  524. else
  525. {
  526. return false;
  527. }
  528. }
  529. public function onFCUnsubscribe(info:Object):void
  530. {
  531. trace(("onFCUnSubscribe worked" + info));
  532. }
  533. public function onFCSubscribe(info:Object):void
  534. {
  535. trace(("onFCSubscribe worked" + info));
  536. }
  537. public function onBWDone(info:Object,nc:NetConnection):void
  538. {
  539. if(nc.connected) {
  540. myConnection=nc;
  541. trace(((("onBWDone " + info) + " :: ") + myStatus.src));
  542. var customClient:Object = new Object ;
  543. customClient.onMetaData = onMetaDataHandler;
  544. customClient.onPlayStatus = onPlayStatusHandler;// According to the forums and my tests, onPlayStatus only works with FMS (Flash Media Server).
  545. myStream = null;
  546. myStream = new NetStream(myConnection);
  547. myStream.addEventListener(NetStatusEvent.NET_STATUS,netStatusHandler);
  548. myStream.client = customClient;
  549. if(connectString.streamTYPE == "LIVE") {
  550. myStream.bufferTime = 3; // was 3
  551. myStream.bufferTimeMax = 24;
  552. startBuffer = 3;
  553. maxBuffer = 12;
  554. } else {
  555. myStream.bufferTime = .2; // was 3
  556. myStream.bufferTimeMax = 0;
  557. startBuffer = .2;
  558. maxBuffer = 12;
  559. }
  560. //streamName="";
  561. //var connectString:Object = parseRTMPsrcConnect(myStatus.src);
  562. //streamName=connectString.streamFileName;
  563. responder = new Responder(onResult);
  564. myConnection.call("getStreamLength",responder,streamName);
  565. } else {
  566. connectStream();
  567. }
  568. trace("PLAY SOURCE: "+connectString);
  569. }
  570. public function play(time:Number = NaN):Boolean {
  571. //trace("PLAY: "+time+" - isPlaying: "+myStatus.isPlaying +" - myStatus.isStartingDownload:"+myStatus.isStartingDownload);
  572. var wasPlaying:Boolean = myStatus.isPlaying;
  573. if(!isNaN(time) && myStatus.srcSet) {
  574. if(myStatus.isPlaying) {
  575. myStream.pause();
  576. myStatus.isPlaying = false;
  577. }
  578. myStatus.pausePosition = time;
  579. }
  580. if(myStatus.isStartingDownload) {
  581. myStatus.playOnLoad = true; // Raise flag, captured in onMetaDataHandler()
  582. return true;
  583. } else if(myStatus.loadRequired()) {
  584. myStatus.playOnLoad = true; // Raise flag, captured in onMetaDataHandler()
  585. return load();
  586. } else if((myStatus.isLoading || myStatus.isLoaded) && !myStatus.isPlaying) {
  587. if(myStatus.metaDataReady && myStatus.pausePosition > myStatus.duration && connectString.streamTYPE != "LIVE") { // The time is invalid, ie., past the end.
  588. myStream.pause(); // Since it is playing by default at this point.
  589. myStatus.pausePosition = 0;
  590. trace("SEEKER!");
  591. myStream.seek(0);
  592. timeUpdates(false);
  593. timeUpdateEvent();
  594. if(wasPlaying) { // For when playing and then get a play(huge)
  595. this.dispatchEvent(new JplayerEvent(JplayerEvent.JPLAYER_PAUSE, myStatus));
  596. }
  597. } else if(getSeekTimeRatio() > getLoadRatio()) { // Use an estimate based on the downloaded amount
  598. myStatus.playOnSeek = true;
  599. seeking(true);
  600. trace("SEEKER PAUSE!");
  601. myStream.pause(); // Since it is playing by default at this point.
  602. } else {
  603. if(!isNaN(time)) { // Avoid using seek() when it is already correct.
  604. trace("SEEKER3");
  605. myStream.seek(myStatus.pausePosition/1000); // Since time is in ms and seek() takes seconds
  606. }
  607. myStatus.isPlaying = true; // Set immediately before playing. Could affects events.
  608. trace("SHOULD GET RESUME!");
  609. myStream.resume();
  610. timeUpdates(true);
  611. if(!wasPlaying) {
  612. this.dispatchEvent(new JplayerEvent(JplayerEvent.JPLAYER_PLAY, myStatus));
  613. }
  614. }
  615. return true;
  616. } else {
  617. return false;
  618. }
  619. }
  620. public function pause(time:Number=NaN):Boolean
  621. {
  622. //trace("PAUSE: "+time);
  623. myStatus.playOnLoad = false;// Reset flag in case load/play issued immediately before this command, ie., before onMetadata() event.
  624. myStatus.playOnSeek = false;// Reset flag in case play(time) issued before the command and is still seeking to time set.
  625. var wasPlaying:Boolean = myStatus.isPlaying;
  626. // To avoid possible loops with timeupdate and pause(time). A pause() does not have the problem.
  627. var alreadyPausedAtTime:Boolean = false;
  628. if(!isNaN(time) && myStatus.pausePosition == time) {
  629. alreadyPausedAtTime = true;
  630. }
  631. trace("!isNaN: "+!isNaN(time) +" isNaN: "+isNaN(time));
  632. // Need to wait for metadata to load before ever issuing a pause. The metadata handler will call this function if needed, when ready.
  633. if (((myStream != null) && myStatus.metaDataReady))
  634. {// myStream is a null until the 1st media is loaded. ie., The 1st ever setMedia being followed by a pause() or pause(t).
  635. if(connectString.streamTYPE == "LIVE") {
  636. trace("PAUSING LIVE");
  637. myStream.play(false)
  638. } else {
  639. trace("PAUSING RECORDED");
  640. myStream.pause();
  641. }
  642. }
  643. if (myStatus.isPlaying)
  644. {
  645. myStatus.isPlaying = false;
  646. myStatus.pausePosition = myStream.time * 1000;
  647. }
  648. if (! isNaN(time) && myStatus.srcSet)
  649. {
  650. myStatus.pausePosition = time;
  651. }
  652. if (wasPlaying)
  653. {
  654. this.dispatchEvent(new JplayerEvent(JplayerEvent.JPLAYER_PAUSE,myStatus));
  655. }
  656. if (myStatus.isStartingDownload)
  657. {
  658. return true;
  659. }
  660. else if (myStatus.loadRequired())
  661. {
  662. if ((time > 0))
  663. {// We do not want the stop() command, which does pause(0), causing a load operation.
  664. return load();
  665. }
  666. else
  667. {
  668. return true;// Technically the pause(0) succeeded. ie., It did nothing, since nothing was required.
  669. }
  670. }
  671. else if (myStatus.isLoading || myStatus.isLoaded)
  672. {
  673. if (myStatus.metaDataReady && myStatus.pausePosition > myStatus.duration && connectString.streamTYPE != "LIVE" )
  674. {// The time is invalid, ie., past the end.
  675. myStatus.pausePosition = 0;
  676. trace("GOT HERE!");
  677. myStream.seek(0);
  678. seekedEvent();// Deals with seeking effect when using setMedia() then pause(huge). NB: There is no preceeding seeking event.
  679. }
  680. else if (! isNaN(time))
  681. {
  682. if ((getSeekTimeRatio() > getLoadRatio()))
  683. {// Use an estimate based on the downloaded amount
  684. seeking(true);
  685. }
  686. else
  687. {
  688. if (myStatus.metaDataReady && connectString.streamTYPE != "LIVE")
  689. {// Otherwise seek(0) will stop the metadata loading.
  690. trace("GOT HERE TOO!");
  691. myStream.seek(myStatus.pausePosition / 1000);
  692. }
  693. }
  694. }
  695. timeUpdates(false);
  696. // Need to be careful with timeupdate event, otherwise a pause in a timeupdate event can cause a loop.
  697. // Neither pause() nor pause(time) will cause a timeupdate loop.
  698. if(wasPlaying || !isNaN(time) && !alreadyPausedAtTime) {
  699. timeUpdateEvent();
  700. }
  701. return true;
  702. }
  703. else
  704. {
  705. return false;
  706. }
  707. }
  708. public function playHead(percent:Number):Boolean
  709. {
  710. var time:Number = percent * getDuration() * getLoadRatio() / 100;
  711. if (myStatus.isPlaying || myStatus.playOnLoad || myStatus.playOnSeek)
  712. {
  713. return play(time);
  714. }
  715. else
  716. {
  717. return pause(time);
  718. }
  719. }
  720. public function setVolume(v:Number):void
  721. {
  722. myStatus.volume = v;
  723. myTransform.volume = v;
  724. if ((myStream != null))
  725. {
  726. myStream.soundTransform = myTransform;
  727. }
  728. }
  729. private function updateStatusValues():void
  730. {
  731. //myStatus.seekPercent = 100 * getLoadRatio();
  732. myStatus.seekPercent = 100;
  733. myStatus.currentTime = getCurrentTime();
  734. myStatus.currentPercentRelative = 100 * getCurrentRatioRel();
  735. myStatus.currentPercentAbsolute = 100 * getCurrentRatioAbs();
  736. myStatus.duration = getDuration();
  737. }
  738. public function getLoadRatio():Number
  739. {
  740. return 1;
  741. /*trace("LoadRatio:"+myStream.bytesLoaded, myStream.bytesTotal);
  742. if((myStatus.isLoading || myStatus.isLoaded) && myStream.bytesTotal > 0) {
  743. return myStream.bytesLoaded / myStream.bytesTotal;
  744. } else {
  745. return 0;
  746. }
  747. */
  748. }
  749. public function getDuration():Number
  750. {
  751. return myStatus.duration;// Set from meta data.
  752. }
  753. public function getCurrentTime():Number
  754. {
  755. if (myStatus.isPlaying)
  756. {
  757. //trace(myStream.time * 1000);
  758. return myStream.time * 1000; // was +1000
  759. }
  760. else
  761. {
  762. return myStatus.pausePosition;
  763. }
  764. }
  765. public function getCurrentRatioRel():Number
  766. {
  767. if ((getCurrentRatioAbs() <= getLoadRatio()))
  768. {
  769. //if((getLoadRatio() > 0) && (getCurrentRatioAbs() <= getLoadRatio())) {
  770. return getCurrentRatioAbs() / getLoadRatio();
  771. }
  772. else
  773. {
  774. return 0;
  775. }
  776. }
  777. public function getCurrentRatioAbs():Number
  778. {
  779. if ((getDuration() > 0))
  780. {
  781. return getCurrentTime() / getDuration();
  782. }
  783. else
  784. {
  785. return 0;
  786. }
  787. }
  788. public function getSeekTimeRatio():Number
  789. {
  790. if ((getDuration() > 0))
  791. {
  792. return myStatus.pausePosition / getDuration();
  793. }
  794. else
  795. {
  796. return 1;
  797. }
  798. }
  799. public function onPlayStatusHandler(infoObject:Object):void
  800. {
  801. trace((("OnPlayStatusHandler called: (" + getTimer()) + " ms)"));
  802. for (var prop:* in infoObject)
  803. {
  804. trace(((("\t" + prop) + ":\t") + infoObject[prop]));
  805. }
  806. if (infoObject.code == "NetStream.Play.Complete")
  807. {
  808. endedEvent();
  809. }
  810. }
  811. public function onMetaDataHandler(info:Object):void
  812. {// Used in connectStream() in myStream.client object.
  813. // This event occurs when jumping to the start of static files! ie., seek(0) will cause this event to occur.
  814. if (! myStatus.metaDataReady)
  815. {
  816. trace("\n\n*** METADATA FIRED! ***\n\n");
  817. //this.dispatchEvent(new JplayerEvent(JplayerEvent.DEBUG_MSG,myStatus,"onMetaDataHandler: " + info.duration + " | " + info.width + "x" + info.height));
  818. myStatus.metaDataReady = true;// Set flag so that this event only effects jPlayer the 1st time.
  819. myStatus.metaData = info;
  820. myStatus.duration = info.duration * 1000;// Only available via Meta Data.
  821. if (info.width != undefined)
  822. {
  823. myVideo.width = myStatus.videoWidth = info.width;
  824. }
  825. if (info.height != undefined)
  826. {
  827. myVideo.height = myStatus.videoHeight = info.height;
  828. }
  829. if (myStatus.playOnLoad)
  830. {
  831. myStatus.playOnLoad = false;// Capture the flag
  832. if (myStatus.pausePosition > 0)
  833. {// Important for setMedia followed by play(time).
  834. play(myStatus.pausePosition);
  835. }
  836. else
  837. {
  838. play();// Not always sending pausePosition avoids the extra seek(0) for a normal play() command.
  839. }
  840. }
  841. else
  842. {
  843. 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)
  844. }
  845. this.dispatchEvent(new JplayerEvent(JplayerEvent.JPLAYER_LOADEDMETADATA,myStatus));
  846. }
  847. else
  848. {
  849. //this.dispatchEvent(new JplayerEvent(JplayerEvent.DEBUG_MSG,myStatus,"onMetaDataHandler: Already read (NO EFFECT)"));
  850. }
  851. }
  852. }
  853. }